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 com.kuro.whale.xposed;
  
  import java.lang.reflect.AccessibleObject;
  import java.lang.reflect.Member;
  import java.lang.reflect.Modifier;
  
  /**
   * Contains common code for working with Methods/Constructor, extracted and
   * refactored from <code>MethodUtils</code> when it was imported from Commons
   * BeanUtils.
   *
   * @version $Id: MemberUtils.java 1143537 2011-07-06 19:30:22Z joehni $
   * @since 2.5
   */
  @SuppressWarnings("all")
  public abstract class MemberUtils {
      // TODO extract an interface to implement compareParameterSets(...)?
  
      private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
  
      /**
       * Array of primitive number types ordered by "promotability"
       */
      private static final Class<?>[] ORDERED_PRIMITIVE_TYPES = {Byte.TYPE, Short.TYPE,
              Character.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE};
  
      /**
       * XXX Default access superclass workaround
       * <p>
       * When a public class has a default access superclass with public members,
       * these members are accessible. Calling them from compiled code works fine.
       * Unfortunately, on some JVMs, using reflection to invoke these members
       * seems to (wrongly) prevent access even when the modifier is public.
       * Calling setAccessible(true) solves the problem but will only work from
       * sufficiently privileged code. Better workarounds would be gratefully
       * accepted.
       *
       * @param o the AccessibleObject to set as accessible
       */
      static void setAccessibleWorkaround(AccessibleObject o) {
          if (o == null || o.isAccessible()) {
              return;
          }
          Member m = (Member) o;
          if (Modifier.isPublic(m.getModifiers())
                  && isPackageAccess(m.getDeclaringClass().getModifiers())) {
              try {
                  o.setAccessible(true);
              } catch (SecurityException e) { // NOPMD
                  // ignore in favor of subsequent IllegalAccessException
              }
          }
      }
  
      /**
       * Returns whether a given set of modifiers implies package access.
       *
       * @param modifiers to test
       * @return true unless package/protected/private modifier detected
       */
      static boolean isPackageAccess(int modifiers) {
          return (modifiers & ACCESS_TEST) == 0;
      }
  
      /**
       * Returns whether a Member is accessible.
       *
       * @param m Member to check
       * @return true if <code>m</code> is accessible
       */
      static boolean isAccessible(Member m) {
          return m != null && Modifier.isPublic(m.getModifiers()) && !m.isSynthetic();
      }
  
      /**
       * Compares the relative fitness of two sets of parameter types in terms of
       * matching a third set of runtime parameter types, such that a list ordered
       * by the results of the comparison would return the best match first
       * (least).
       *
       * @param left   the "left" parameter set
       * @param right  the "right" parameter set
       * @param actual the runtime parameter types to match against
       *               <code>left</code>/<code>right</code>
       * @return int consistent with <code>compare</code> semantics
       */
      public static int compareParameterTypes(Class<?>[] left, Class<?>[] right, Class<?>[] actual) {
          float leftCost = getTotalTransformationCost(actual, left);
          float rightCost = getTotalTransformationCost(actual, right);
          return leftCost < rightCost ? -1 : rightCost < leftCost ? 1 : 0;
      }
  
      /**
       * Returns the sum of the object transformation cost for each class in the
       * source argument list.
       *
       * @param srcArgs  The source arguments
       * @param destArgs The destination arguments
       * @return The total transformation cost
       */
      private static float getTotalTransformationCost(Class<?>[] srcArgs, Class<?>[] destArgs) {
          float totalCost = 0.0f;
          for (int i = 0; i < srcArgs.length; i++) {
              Class<?> srcClass, destClass;
              srcClass = srcArgs[i];
              destClass = destArgs[i];
              totalCost += getObjectTransformationCost(srcClass, destClass);
          }
          return totalCost;
      }
  
      /**
       * Gets the number of steps required needed to turn the source class into
       * the destination class. This represents the number of steps in the object
       * hierarchy graph.
       *
       * @param srcClass  The source class
       * @param destClass The destination class
       * @return The cost of transforming an object
       */
      private static float getObjectTransformationCost(Class<?> srcClass, Class<?> destClass) {
          if (destClass.isPrimitive()) {
              return getPrimitivePromotionCost(srcClass, destClass);
          }
          float cost = 0.0f;
          while (srcClass != null && !destClass.equals(srcClass)) {
              if (destClass.isInterface() && ClassUtils.isAssignable(srcClass, destClass)) {
                  // slight penalty for interface match.
                  // we still want an exact match to override an interface match,
                  // but
                  // an interface match should override anything where we have to
                  // get a superclass.
                  cost += 0.25f;
                  break;
              }
              cost++;
              srcClass = srcClass.getSuperclass();
          }
          /*
           * If the destination class is null, we've travelled all the way up to
           * an Object match. We'll penalize this by adding 1.5 to the cost.
           */
          if (srcClass == null) {
              cost += 1.5f;
          }
          return cost;
      }
  
      /**
       * Gets the number of steps required to promote a primitive number to another
       * type.
       *
       * @param srcClass  the (primitive) source class
       * @param destClass the (primitive) destination class
       * @return The cost of promoting the primitive
       */
      private static float getPrimitivePromotionCost(final Class<?> srcClass, final Class<?> destClass) {
          float cost = 0.0f;
          Class<?> cls = srcClass;
          if (!cls.isPrimitive()) {
              // slight unwrapping penalty
              cost += 0.1f;
              cls = ClassUtils.wrapperToPrimitive(cls);
          }
          for (int i = 0; cls != destClass && i < ORDERED_PRIMITIVE_TYPES.length; i++) {
              if (cls == ORDERED_PRIMITIVE_TYPES[i]) {
                  cost += 0.1f;
                  if (i < ORDERED_PRIMITIVE_TYPES.length - 1) {
                      cls = ORDERED_PRIMITIVE_TYPES[i + 1];
                  }
              }
          }
          return cost;
      }
  
  }
  