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.activemq.tool.properties;
  
  import java.lang.reflect.Constructor;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Properties;
  import java.util.StringTokenizer;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public final class ReflectionUtil {
      private static final Logger LOG = LoggerFactory.getLogger(ReflectionUtil.class);
  
      private ReflectionUtil() {
      }
  
      public static void configureClass(Object obj, String key, String val) {
          try {
              String debugInfo;
  
              Object target = obj;
              Class<?> targetClass = obj.getClass();
  
              // DEBUG: Debugging Info
              debugInfo = "Invoking: " + targetClass.getName();
  
              StringTokenizer tokenizer = new StringTokenizer(key, ".");
              String keySubString = key;
              int tokenCount = tokenizer.countTokens();
  
              // For nested settings, get the object first. -1, do not count the
              // last token
              for (int j = 0; j < tokenCount - 1; j++) {
                  // Find getter method first
                  String name = tokenizer.nextToken();
  
                  // Check if the target object will accept the settings
                  if (target instanceof ReflectionConfigurable && !((ReflectionConfigurable)target).acceptConfig(keySubString, val)) {
                      return;
                  } else {
                      // This will reduce the key, so that it will be recognize by
                      // the next object. i.e.
                      // Property name: factory.prefetchPolicy.queuePrefetch
                      // Calling order:
                      // this.getFactory().prefetchPolicy().queuePrefetch();
                      // If factory does not accept the config, it should be given
                      // prefetchPolicy.queuePrefetch as the key
                      // +1 to account for the '.'
                      keySubString = keySubString.substring(name.length() + 1);
                  }
  
                  String getMethod = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
                  Method method = targetClass.getMethod(getMethod, new Class[] {});
                  target = method.invoke(target, null);
                  targetClass = target.getClass();
  
                  debugInfo += "." + getMethod + "()";
              }
  
              // Property name
              String property = tokenizer.nextToken();
              // Check if the target object will accept the settings
              if (target instanceof ReflectionConfigurable && !((ReflectionConfigurable)target).acceptConfig(property, val)) {
                  return;
              }
  
              // Find setter method
              Method setterMethod = findSetterMethod(targetClass, property);
  
              // Get the first parameter type. This assumes that there is only one
              // parameter.
              if (setterMethod == null) {
                  throw new IllegalAccessException("Unable to find appropriate setter method signature for property: " + property);
              }
              Class<?> paramType = setterMethod.getParameterTypes()[0];
  
              // Set primitive type
              debugInfo += "." + setterMethod + "(" + paramType.getName() + ": " + val + ")";
              if (paramType.isPrimitive()) {
                  if (paramType == Boolean.TYPE) {
                      setterMethod.invoke(target, new Object[] {
                          Boolean.valueOf(val)
                      });
                  } else if (paramType == Integer.TYPE) {
                      setterMethod.invoke(target, new Object[] {
                          Integer.valueOf(val)
                      });
                  } else if (paramType == Long.TYPE) {
                      setterMethod.invoke(target, new Object[] {
                          Long.valueOf(val)
                      });
                  } else if (paramType == Double.TYPE) {
                      setterMethod.invoke(target, new Object[] {
                          Double.valueOf(val)
                      });
                  } else if (paramType == Float.TYPE) {
                      setterMethod.invoke(target, new Object[] {
                          Float.valueOf(val)
                      });
                  } else if (paramType == Short.TYPE) {
                      setterMethod.invoke(target, new Object[] {
                          Short.valueOf(val)
                      });
                  } else if (paramType == Byte.TYPE) {
                      setterMethod.invoke(target, new Object[] {
                          Byte.valueOf(val)
                      });
                  } else if (paramType == Character.TYPE) {
                      setterMethod.invoke(target, new Object[] {
                              val.charAt(0)
                      });
                  }
              } else {
                  // Set String type
                  if (paramType == String.class) {
                      setterMethod.invoke(target, new Object[] {
                          val
                      });
  
                      // For unknown object type, try to create an instance of the
                      // object using a String constructor
                  } else {
                      Constructor c = paramType.getConstructor(new Class[] {
                          String.class
                      });
                      Object paramObject = c.newInstance(new Object[] {
                          val
                      });
  
                      setterMethod.invoke(target, new Object[] {
                          paramObject
                      });
                  }
              }
              LOG.debug(debugInfo);
  
          } catch (Exception e) {
              LOG.warn(e.toString());
          }
      }
  
      public static void configureClass(Object obj, Properties props) {
          for (Iterator<Object> i = props.keySet().iterator(); i.hasNext();) {
              try {
                  String key = (String)i.next();
                  String val = props.getProperty(key);
  
                  configureClass(obj, key, val);
              } catch (Throwable t) {
                  // Let's catch any exception as this could be cause by the
                  // foreign class
                  t.printStackTrace();
              }
          }
      }
  
      public static Properties retrieveObjectProperties(Object obj) {
          Properties props = new Properties();
          try {
              props.putAll(retrieveClassProperties("", obj.getClass(), obj));
          } catch (Exception e) {
              LOG.warn(e.toString());
          }
          return props;
      }
  
      protected static Properties retrieveClassProperties(String prefix, Class targetClass, Object targetObject) {
          if (targetClass == null || targetObject == null) {
              return new Properties();
          } else {
              Properties props = new Properties();
              Method[] getterMethods = findAllGetterMethods(targetClass);
              for (int i = 0; i < getterMethods.length; i++) {
                  try {
                      String propertyName = getPropertyName(getterMethods[i].getName());
                      Class retType = getterMethods[i].getReturnType();
  
                      // If primitive or string type, return it
                      if (retType.isPrimitive() || retType == String.class) {
                          // Check for an appropriate setter method to consider it
                          // as a property
                          if (findSetterMethod(targetClass, propertyName) != null) {
                              Object val = null;
                              try {
                                  val = getterMethods[i].invoke(targetObject, null);
                              } catch (InvocationTargetException e) {
                                  e.printStackTrace();
                              } catch (IllegalAccessException e) {
                                  e.printStackTrace();
                              }
                              props.setProperty(prefix + propertyName, val + "");
                          }
                      } else {
                          try {
                              Object val = getterMethods[i].invoke(targetObject, null);
                              if (val != null && val != targetObject) {
                                  props.putAll(retrieveClassProperties(propertyName + ".", val.getClass(), val));
                              }
                          } catch (InvocationTargetException e) {
                              e.printStackTrace();
                          } catch (IllegalAccessException e) {
                              e.printStackTrace();
                          }
                      }
                  } catch (Throwable t) {
                      // Let's catch any exception, cause this could be cause by
                      // the foreign class
                      t.printStackTrace();
                  }
              }
              return props;
          }
      }
  
      private static Method findSetterMethod(Class targetClass, String propertyName) {
          String methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
  
          Method[] methods = targetClass.getMethods();
          for (int i = 0; i < methods.length; i++) {
              if (methods[i].getName().equals(methodName) && isSetterMethod(methods[i])) {
                  return methods[i];
              }
          }
          return null;
      }
  
      private static Method findGetterMethod(Class targetClass, String propertyName) {
          String methodName1 = "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
          String methodName2 = "is" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
  
          Method[] methods = targetClass.getMethods();
          for (int i = 0; i < methods.length; i++) {
              if ((methods[i].getName().equals(methodName1) || methods[i].getName().equals(methodName2)) && isGetterMethod(methods[i])) {
                  return methods[i];
              }
          }
          return null;
      }
  
      private static Method[] findAllGetterMethods(Class targetClass) {
          List getterMethods = new ArrayList();
          Method[] methods = targetClass.getMethods();
  
          for (int i = 0; i < methods.length; i++) {
              if (isGetterMethod(methods[i])) {
                  getterMethods.add(methods[i]);
              }
          }
  
          return (Method[])getterMethods.toArray(new Method[] {});
      }
  
      private static boolean isGetterMethod(Method method) {
          // Check method signature first
          // If 'get' method, must return a non-void value
          // If 'is' method, must return a boolean value
          // Both must have no parameters
          // Method must not belong to the Object class to prevent infinite loop
          return ((method.getName().startsWith("is") && method.getReturnType() == Boolean.TYPE) || (method.getName().startsWith("get") && method.getReturnType() != Void.TYPE))
                 && (method.getParameterTypes().length == 0) && method.getDeclaringClass() != Object.class;
      }
  
      private static boolean isSetterMethod(Method method) {
          // Check method signature first
          if (method.getName().startsWith("set") && method.getReturnType() == Void.TYPE) {
              Class[] paramType = method.getParameterTypes();
              // Check that it can only accept one parameter
              if (paramType.length == 1) {
                  // Check if parameter is a primitive or can accept a String
                  // parameter
                  if (paramType[0].isPrimitive() || paramType[0] == String.class) {
                      return true;
                  } else {
                      // Check if object can accept a string as a constructor
                      try {
                          if (paramType[0].getConstructor(new Class[] {
                              String.class
                          }) != null) {
                              return true;
                          }
                      } catch (NoSuchMethodException e) {
                          // Do nothing
                      }
                  }
              }
          }
          return false;
      }
  
      private static String getPropertyName(String methodName) {
          String name;
          if (methodName.startsWith("get")) {
              name = methodName.substring(3);
          } else if (methodName.startsWith("set")) {
              name = methodName.substring(3);
          } else if (methodName.startsWith("is")) {
              name = methodName.substring(2);
          } else {
              name = "";
          }
  
          return name.substring(0, 1).toLowerCase() + name.substring(1);
      }
  }
  