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

org.hibernate.validator.internal.util.ExecutableHelper Maven / Gradle / Ivy

Go to download

JPMS Module-Info's for a few of the Jakarta Libraries. These will be removed as time goes by

There is a newer version: 62
Show newest version
/*
 * Hibernate Validator, declare and validate application constraints
 *
 * License: Apache License, Version 2.0
 * See the license.txt file in the root directory or .
 */
package org.hibernate.validator.internal.util;

import java.lang.annotation.ElementType;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;

import org.hibernate.validator.internal.properties.Callable;
import org.hibernate.validator.internal.properties.Signature;
import org.hibernate.validator.internal.util.classhierarchy.Filters;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.internal.util.privilegedactions.GetResolvedMemberMethods;

import com.fasterxml.classmate.Filter;
import com.fasterxml.classmate.MemberResolver;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.ResolvedTypeWithMembers;
import com.fasterxml.classmate.TypeResolver;
import com.fasterxml.classmate.members.RawMethod;
import com.fasterxml.classmate.members.ResolvedMethod;

/**
 * Provides shared functionality dealing with executables.
 *
 * @author Hardy Ferentschik
 * @author Gunnar Morling
 * @author Kevin Pollet <[email protected]> (C) 2011 SERLI
 */
public final class ExecutableHelper {

	private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
	private final TypeResolver typeResolver;

	public ExecutableHelper(TypeResolutionHelper typeResolutionHelper) {
		this.typeResolver = typeResolutionHelper.getTypeResolver();
	}

	public boolean overrides(Callable subTypeMethod, Callable superTypeMethod) {
		return subTypeMethod.overrides( this, superTypeMethod );
	}

	/**
	 * Checks, whether {@code subTypeMethod} overrides {@code superTypeMethod}.
	 *
	 * @param subTypeMethod The sub type method (cannot be {@code null}).
	 * @param superTypeMethod The super type method (cannot be {@code null}).
	 *
	 * @return Returns {@code true} if {@code subTypeMethod} overrides {@code superTypeMethod},
	 * {@code false} otherwise.
	 */
	public boolean overrides(Method subTypeMethod, Method superTypeMethod) {
		Contracts.assertValueNotNull( subTypeMethod, "subTypeMethod" );
		Contracts.assertValueNotNull( superTypeMethod, "superTypeMethod" );

		if ( subTypeMethod.equals( superTypeMethod ) ) {
			return false;
		}

		if ( !subTypeMethod.getName().equals( superTypeMethod.getName() ) ) {
			return false;
		}

		if ( subTypeMethod.getParameterCount() != superTypeMethod.getParameterCount() ) {
			return false;
		}

		if ( !superTypeMethod.getDeclaringClass().isAssignableFrom( subTypeMethod.getDeclaringClass() ) ) {
			return false;
		}

		if ( Modifier.isStatic( superTypeMethod.getModifiers() ) || Modifier.isStatic(
				subTypeMethod.getModifiers()
		) ) {
			return false;
		}

		// HV-861 Bridge method should be ignored. Classmates type/member resolution will take care of proper
		// override detection without considering bridge methods
		if ( subTypeMethod.isBridge() ) {
			return false;
		}

		if ( Modifier.isPrivate( superTypeMethod.getModifiers() ) ) {
			return false;
		}

		if ( !isMethodVisibleTo( superTypeMethod, subTypeMethod ) ) {
			return false;
		}

		return instanceMethodParametersResolveToSameTypes( subTypeMethod, superTypeMethod );
	}

	/**
	 * Checks if a pair of given methods ({@code left} and {@code right}) are resolved to the same
	 * method based on the {@code mainSubType} type.
	 *
	 * @param mainSubType a type at the bottom of class hierarchy to be used to lookup the methods.
	 * @param left one of the methods to check
	 * @param right another of the methods to check
	 *
	 * @return {@code true} if a pair of methods are equal {@code left == right}, or one of the methods
	 * 		override another one in the class hierarchy with {@code mainSubType} at the bottom,
	 * 		{@code false} otherwise.
	 */
	public boolean isResolvedToSameMethodInHierarchy(Class mainSubType, Method left, Method right) {
		Contracts.assertValueNotNull( mainSubType, "mainSubType" );
		Contracts.assertValueNotNull( left, "left" );
		Contracts.assertValueNotNull( right, "right" );

		if ( left.equals( right ) ) {
			return true;
		}

		if ( !left.getName().equals( right.getName() ) ) {
			return false;
		}

		// methods with same name in the same class should be different
		if ( left.getDeclaringClass().equals( right.getDeclaringClass() ) ) {
			return false;
		}

		if ( left.getParameterCount() != right.getParameterCount() ) {
			return false;
		}

		// if at least one method from a pair is static - they are different methods
		if ( Modifier.isStatic( right.getModifiers() ) || Modifier.isStatic( left.getModifiers() ) ) {
			return false;
		}

		// HV-861 Bridge method should be ignored. Classmates type/member resolution will take care of proper
		// override detection without considering bridge methods
		if ( left.isBridge() || right.isBridge() ) {
			return false;
		}

		// if one of the methods is private - methods are different
		if ( Modifier.isPrivate( left.getModifiers() ) || Modifier.isPrivate( right.getModifiers() ) ) {
			return false;
		}

		if ( !isMethodVisibleTo( right, left ) || !isMethodVisibleTo( left, right ) ) {
			return false;
		}

		// We need to check if the passed mainSubType is not a Weld proxy. In case of proxy we need to get
		// a class that was proxied otherwise we can use the class itself. This is due to the issue that
		// call to Class#getGenericInterfaces() on a Weld proxy returns raw types instead of parametrized
		// generics and methods will not be resolved correctly.
		return instanceMethodParametersResolveToSameTypes(
				Filters.excludeProxies().accepts( mainSubType ) ? mainSubType : mainSubType.getSuperclass(),
				left,
				right
		);
	}

	private static boolean isMethodVisibleTo(Method visibleMethod, Method otherMethod) {
		return Modifier.isPublic( visibleMethod.getModifiers() ) || Modifier.isProtected( visibleMethod.getModifiers() )
				|| visibleMethod.getDeclaringClass().getPackage().equals( otherMethod.getDeclaringClass().getPackage() );
	}

	public static String getSimpleName(Executable executable) {
		return executable instanceof Constructor ? executable.getDeclaringClass().getSimpleName() : executable.getName();
	}

	public static Signature getSignature(Executable executable) {
		return getSignature( getSimpleName( executable ), executable.getParameterTypes() );
	}

	public static Signature getSignature(String name, Class[] parameterTypes) {
		return new Signature( name, parameterTypes );
	}

	/**
	 * Returns a string representation of an executable with the given name and parameter types in the form
	 * {@code ( ...  )}, e.g. for logging purposes.
	 *
	 * @param name the name of the executable
	 * @param parameterTypes the types of the executable's parameters
	 *
	 * @return A string representation of the given executable.
	 */
	public static String getExecutableAsString(String name, Class... parameterTypes) {
		StringBuilder signature = new StringBuilder( name.length() + 2 + parameterTypes.length * 25 );
		signature.append( name ).append( '(' );
		boolean separator = false;
		for ( Class parameterType : parameterTypes ) {
			if ( separator ) {
				signature.append( ", " );
			}
			else {
				separator = true;
			}
			signature.append( parameterType.getSimpleName() );
		}
		signature.append( ')' );
		return signature.toString();
	}

	public static ElementType getElementType(Executable executable) {
		return executable instanceof Constructor ? ElementType.CONSTRUCTOR : ElementType.METHOD;
	}

	/**
	 * Whether the parameters of the two given instance methods resolve to the same types or not. Takes type parameters into account.
	 *
	 * @param subTypeMethod a method on a supertype
	 * @param superTypeMethod a method on a subtype
	 *
	 * @return {@code true} if the parameters of the two methods resolve to the same types, {@code false otherwise}.
	 */
	private boolean instanceMethodParametersResolveToSameTypes(Method subTypeMethod, Method superTypeMethod) {
		return instanceMethodParametersResolveToSameTypes( subTypeMethod.getDeclaringClass(), subTypeMethod, superTypeMethod );
	}

	private boolean instanceMethodParametersResolveToSameTypes(Class mainSubType, Method left, Method right) {
		if ( left.getParameterCount() == 0 ) {
			return true;
		}

		ResolvedType resolvedSubType = typeResolver.resolve( mainSubType );

		MemberResolver memberResolver = new MemberResolver( typeResolver );
		memberResolver.setMethodFilter( new SimpleMethodFilter( left, right ) );
		ResolvedTypeWithMembers typeWithMembers = memberResolver.resolve(
				resolvedSubType,
				null,
				null
		);

		// ClassMate itself doesn't require any special permissions, but it invokes reflection APIs which do.
		// Wrapping the call into a privileged action to avoid that all calling code bases need to have the required
		// permission
		ResolvedMethod[] resolvedMethods = run( GetResolvedMemberMethods.action( typeWithMembers ) );

		// The ClassMate doc says that overridden methods are flattened to one
		// resolved method. But that is the case only for methods without any
		// generic parameters.
		if ( resolvedMethods.length == 1 ) {
			return true;
		}

		// For methods with generic parameters I have to compare the argument
		// types (which are resolved) of the two filtered member methods.
		try {
			for ( int i = 0; i < resolvedMethods[0].getArgumentCount(); i++ ) {
				if ( !resolvedMethods[0].getArgumentType( i )
						.equals( resolvedMethods[1].getArgumentType( i ) ) ) {
					return false;
				}
			}
		}
		// Putting this in as a safe guard for HV-861. In case the issue occurs again we will have some
		// better information
		catch (ArrayIndexOutOfBoundsException e) {
			LOG.debug(
					"Error in ExecutableHelper#instanceMethodParametersResolveToSameTypes comparing "
							+ left
							+ " with "
							+ right
			);
		}

		return true;
	}

	/**
	 * Runs the given privileged action, using a privileged block if required.
	 * 

* NOTE: This must never be changed into a publicly available method to avoid execution of arbitrary * privileged actions within HV's protection domain. */ private T run(PrivilegedAction action) { return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); } /** * A filter implementation filtering methods matching given methods. * * @author Gunnar Morling */ private static class SimpleMethodFilter implements Filter { private final Method method1; private final Method method2; private SimpleMethodFilter(Method method1, Method method2) { this.method1 = method1; this.method2 = method2; } @Override public boolean include(RawMethod element) { return element.getRawMember().equals( method1 ) || element.getRawMember() .equals( method2 ); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy