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

panda.lang.reflect.Members Maven / Gradle / Ivy

package panda.lang.reflect;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;

import panda.lang.Classes;

/**
 * Contains common code for working with {@link java.lang.reflect.Method Methods}/
 * {@link java.lang.reflect.Constructor Constructors}, extracted and refactored from
 * {@link Methods} when it was imported from Commons BeanUtils.
 */
abstract class Members {
	// 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. When a {@code public} class has a default access
	 * superclass with {@code 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 {@code public}. Calling
	 * {@code 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
	 * @return a boolean indicating whether the accessibility of the object was set to true.
	 */
	static boolean setAccessibleWorkaround(final AccessibleObject o) {
		if (o == null || o.isAccessible()) {
			return false;
		}
		final Member m = (Member)o;
		if (!o.isAccessible() && Modifier.isPublic(m.getModifiers())
				&& isPackageAccess(m.getDeclaringClass().getModifiers())) {
			try {
				o.setAccessible(true);
				return true;
			}
			catch (final SecurityException e) { // NOPMD
				// ignore in favor of subsequent IllegalAccessException
			}
		}
		return false;
	}

	/**
	 * Returns whether a given set of modifiers implies package access.
	 * 
	 * @param modifiers to test
	 * @return {@code true} unless {@code package}/{@code protected}/{@code private} modifier
	 *         detected
	 */
	static boolean isPackageAccess(final int modifiers) {
		return (modifiers & ACCESS_TEST) == 0;
	}

	/**
	 * Returns whether a {@link Member} is accessible.
	 * 
	 * @param m Member to check
	 * @return {@code true} if m is accessible
	 */
	static boolean isAccessible(final 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 right}
	 * @return int consistent with {@code compare} semantics
	 */
	static int compareParameterTypes(final Class[] left, final Class[] right, final Class[] actual) {
		final float leftCost = getTotalTransformationCost(actual, left);
		final 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(final Class[] srcArgs, final 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, final Class destClass) {
		if (destClass.isPrimitive()) {
			return getPrimitivePromotionCost(srcClass, destClass);
		}
		float cost = 0.0f;
		while (srcClass != null && !destClass.equals(srcClass)) {
			if (destClass.isInterface() && Classes.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 = Classes.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;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy