context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Linq;
  using System.Linq.Expressions;
  using System.Reflection;
  using System.Text;
  
  using NHibernate.Properties;
  using NHibernate.Type;
  using NHibernate.Engine;
  
  namespace NHibernate.Util
  {
  	/// <summary>
  	/// Helper class for Reflection related code.
  	/// </summary>
  	public static class ReflectHelper
  	{
  		private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(ReflectHelper));
  
  		public const BindingFlags AnyVisibilityInstance = BindingFlags.Instance | BindingFlags.Public |
  														   BindingFlags.NonPublic;
  
  		private static readonly System.Type[] NoClasses = System.Type.EmptyTypes;
  
  		private static readonly MethodInfo Exception_InternalPreserveStackTrace =
  			typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
  
  		/// <summary>
  		/// Extract the <see cref="MethodInfo"/> from a given expression.
  		/// </summary>
  		/// <typeparam name="TSource">The declaring-type of the method.</typeparam>
  		/// <param name="method">The method.</param>
  		/// <returns>The <see cref="MethodInfo"/> of the no-generic method or the generic-definition for a generic-method.</returns>
  		/// <seealso cref="MethodInfo.GetGenericMethodDefinition"/>
  		public static MethodInfo GetMethodDefinition<TSource>(Expression<Action<TSource>> method)
  		{
  			MethodInfo methodInfo = GetMethod(method);
  			return methodInfo.IsGenericMethod ? methodInfo.GetGenericMethodDefinition() : methodInfo;
  		}
  
  		/// <summary>
  		/// Extract the <see cref="MethodInfo"/> from a given expression.
  		/// </summary>
  		/// <typeparam name="TSource">The declaring-type of the method.</typeparam>
  		/// <param name="method">The method.</param>
  		/// <returns>The <see cref="MethodInfo"/> of the method.</returns>
  		public static MethodInfo GetMethod<TSource>(Expression<Action<TSource>> method)
  		{
  			if (method == null)
  				throw new ArgumentNullException(nameof(method));
  
  			return ((MethodCallExpression)method.Body).Method;
  		}
  
  		/// <summary>
  		/// Extract the <see cref="MethodInfo"/> from a given expression.
  		/// </summary>
  		/// <param name="method">The method.</param>
  		/// <returns>The <see cref="MethodInfo"/> of the no-generic method or the generic-definition for a generic-method.</returns>
  		/// <seealso cref="MethodInfo.GetGenericMethodDefinition"/>
  		public static MethodInfo GetMethodDefinition(Expression<System.Action> method)
  		{
  			MethodInfo methodInfo = GetMethod(method);
  			return methodInfo.IsGenericMethod ? methodInfo.GetGenericMethodDefinition() : methodInfo;
  		}
  
  		/// <summary>
  		/// Extract the <see cref="MethodInfo"/> from a given expression.
  		/// </summary>
  		/// <param name="method">The method.</param>
  		/// <returns>The <see cref="MethodInfo"/> of the method.</returns>
  		public static MethodInfo GetMethod(Expression<System.Action> method)
  		{
  			if (method == null)
  				throw new ArgumentNullException(nameof(method));
  
  			return ((MethodCallExpression)method.Body).Method;
  		}
  
  		/// <summary>
  		/// Get the <see cref="MethodInfo"/> for a public overload of a given method if the method does not match
  		/// given parameter types, otherwise directly yield the given method.
  		/// </summary>
  		/// <param name="method">The method for which finding an overload.</param>
  		/// <param name="parameterTypes">The arguments types of the overload to get.</param>
  		/// <returns>The <see cref="MethodInfo"/> of the method.</returns>
  		/// <remarks>Whenever possible, use GetMethod() instead for performance reasons.</remarks>
  		public static MethodInfo GetMethodOverload(MethodInfo method, params System.Type[] parameterTypes)
  		{
  			if (method == null)
  				throw new ArgumentNullException(nameof(method));
  			if (parameterTypes == null)
  				throw new ArgumentNullException(nameof(parameterTypes));
  
  			if (ParameterTypesMatch(method.GetParameters(), parameterTypes))
  				return method;
  
  			var overload = method.DeclaringType.GetMethod(method.Name,
  				(method.IsStatic ? BindingFlags.Static : BindingFlags.Instance) | BindingFlags.Public,
  				null, parameterTypes, null);
  
  			if (overload == null)
  				throw new InvalidOperationException(
  					$"No overload found for method '{method.DeclaringType.Name}.{method.Name}' and parameter types '{string.Join(", ", parameterTypes.Select(t => t.Name))}'");
  
  			return overload;
  		}
  
  		/// <summary>
  		/// Gets the field or property to be accessed.
  		/// </summary>
  		/// <typeparam name="TSource">The declaring-type of the property.</typeparam>
  		/// <typeparam name="TResult">The type of the property.</typeparam>
  		/// <param name="property">The expression representing the property getter.</param>
  		/// <returns>The <see cref="MemberInfo"/> of the property.</returns>
  		public static MemberInfo GetProperty<TSource, TResult>(Expression<Func<TSource, TResult>> property)
  		{
  			if (property == null)
  			{
  				throw new ArgumentNullException(nameof(property));
  			}
  			return ((MemberExpression)property.Body).Member;
  		}
  
  		internal static bool ParameterTypesMatch(ParameterInfo[] parameters, System.Type[] types)
  		{
  			if (parameters.Length != types.Length)
  			{
  				return false;
  			}
  
  			for (int i = 0; i < parameters.Length; i++)
  			{
  				if (parameters[i].ParameterType == types[i])
  				{
  					continue;
  				}
  
  				if (parameters[i].ParameterType.ContainsGenericParameters && types[i].ContainsGenericParameters &&
  					parameters[i].ParameterType.GetGenericArguments().Length == types[i].GetGenericArguments().Length)
  				{
  					continue;
  				}
  
  				return false;
  			}
  
  			return true;
  		}
  
  		internal static System.Type GetPropertyOrFieldType(this MemberInfo memberInfo)
  		{
  			if (memberInfo is PropertyInfo propertyInfo)
  			{
  				return propertyInfo.PropertyType;
  			}
  
  			if (memberInfo is FieldInfo fieldInfo)
  			{
  				return fieldInfo.FieldType;
  			}
  
  			return null;
  		}
  
  		/// <summary>
  		/// Determine if the specified <see cref="System.Type"/> overrides the
  		/// implementation of Equals from <see cref="Object"/>
  		/// </summary>
  		/// <param name="clazz">The <see cref="System.Type"/> to reflect.</param>
  		/// <returns><see langword="true" /> if any type in the hierarchy overrides Equals(object).</returns>
  		public static bool OverridesEquals(System.Type clazz)
  		{
  			return OverrideMethod(clazz, "Equals", new[] { typeof(object) });
  		}
  
  		private static bool OverrideMethod(System.Type clazz, string methodName, System.Type[] parametersTypes)
  		{
  			try
  			{
  				MethodInfo method = !clazz.IsInterface
  										? clazz.GetMethod(methodName, parametersTypes)
  										: GetMethodFromInterface(clazz, methodName, parametersTypes);
  				if (method == null)
  				{
  					return false;
  				}
  				else
  				{
  					// make sure that the DeclaringType is not System.Object - if that is the
  					// declaring type then there is no override.
  					return !(method.DeclaringType == typeof(object));
  				}
  			}
  			catch (AmbiguousMatchException)
  			{
  				// an ambiguous match means that there is an override and it
  				// can't determine which one to use.
  				return true;
  			}
  		}
  
  		private static MethodInfo GetMethodFromInterface(System.Type type, string methodName, System.Type[] parametersTypes)
  		{
  			const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly;
  			if (type == null)
  			{
  				return null;
  			}
  			MethodInfo method = type.GetMethod(methodName, flags, null, parametersTypes, null);
  			if (method == null)
  			{
  				System.Type[] interfaces = type.GetInterfaces();
  				foreach (var @interface in interfaces)
  				{
  					method = GetMethodFromInterface(@interface, methodName, parametersTypes);
  					if (method != null)
  					{
  						return method;
  					}
  				}
  			}
  			return method;
  		}
  
  		/// <summary>
  		/// Determine if the specified <see cref="System.Type"/> overrides the
  		/// implementation of GetHashCode from <see cref="Object"/>
  		/// </summary>
  		/// <param name="clazz">The <see cref="System.Type"/> to reflect.</param>
  		/// <returns><see langword="true" /> if any type in the hierarchy overrides GetHashCode().</returns>
  		public static bool OverridesGetHashCode(System.Type clazz)
  		{
  			return OverrideMethod(clazz, "GetHashCode", System.Type.EmptyTypes);
  		}
  
  		/// <summary>
  		/// Finds the <see cref="IGetter"/> for the property in the <see cref="System.Type"/>.
  		/// </summary>
  		/// <param name="theClass">The <see cref="System.Type"/> to find the property in.</param>
  		/// <param name="propertyName">The name of the Property to find.</param>
  		/// <param name="propertyAccessorName">The name of the property access strategy.</param>
  		/// <returns>The <see cref="IGetter"/> to get the value of the Property.</returns>
  		/// <remarks>
  		/// This one takes a propertyAccessor name as we might know the correct strategy by now so we avoid Exceptions which are costly
  		/// </remarks>
  		public static IGetter GetGetter(System.Type theClass, string propertyName, string propertyAccessorName)
  		{
  			return PropertyAccessorFactory
  				.GetPropertyAccessor(propertyAccessorName)
  				.GetGetter(theClass, propertyName);
  		}
  
  		/// <summary>
  		/// Get the NHibernate <see cref="IType" /> for the named property of the <see cref="System.Type"/>.
  		/// </summary>
  		/// <param name="theClass">The <see cref="System.Type"/> to find the Property in.</param>
  		/// <param name="name">The name of the property/field to find in the class.</param>
  		/// <param name="access">The name of the property accessor for the property.</param>
  		/// <returns>
  		/// The NHibernate <see cref="IType"/> for the named property.
  		/// </returns>
  		public static IType ReflectedPropertyType(System.Type theClass, string name, string access)
  		{
  			System.Type propertyClass = ReflectedPropertyClass(theClass, name, access);
  
  			var heuristicClass = propertyClass.UnwrapIfNullable();
  
  			return TypeFactory.HeuristicType(heuristicClass.AssemblyQualifiedName);
  		}
  
  		/// <summary>
  		/// Get the <see cref="System.Type" /> for the named property of a type.
  		/// </summary>
  		/// <param name="theClass">The <see cref="System.Type"/> to find the property in.</param>
  		/// <param name="name">The name of the property/field to find in the class.</param>
  		/// <param name="access">The name of the property accessor for the property.</param>
  		/// <returns>The <see cref="System.Type" /> for the named property.</returns>
  		public static System.Type ReflectedPropertyClass(System.Type theClass, string name, string access)
  		{
  			return GetGetter(theClass, name, access).ReturnType;
  		}
  
  		/// <summary>
  		/// Get the <see cref="System.Type" /> for the named property of a type.
  		/// </summary>
  		/// <param name="className">The FullName to find the property in.</param>
  		/// <param name="name">The name of the property/field to find in the class.</param>
  		/// <param name="accessorName">The name of the property accessor for the property.</param>
  		/// <returns>The <see cref="System.Type" /> for the named property.</returns>
  		public static System.Type ReflectedPropertyClass(string className, string name, string accessorName)
  		{
  			try
  			{
  				System.Type clazz = ClassForName(className);
  				return GetGetter(clazz, name, accessorName).ReturnType;
  			}
  			catch (Exception cnfe)
  			{
  				throw new MappingException(string.Format("class {0} not found while looking for property: {1}", className, name), cnfe);
  			}
  		}
  
  		/// <summary>
  		/// Returns a reference to the Type.
  		/// </summary>
  		/// <param name="name">The name of the class or a fully qualified name.</param>
  		/// <returns>The Type for the Class.</returns>
  		public static System.Type ClassForName(string name)
  		{
  			AssemblyQualifiedTypeName parsedName = TypeNameParser.Parse(name);
  			System.Type result = TypeFromAssembly(parsedName, true);
  			return result;
  		}
  
  		/// <summary>
  		/// Load a System.Type given its name.
  		/// </summary>
  		/// <param name="classFullName">The class FullName or AssemblyQualifiedName</param>
  		/// <returns>The System.Type</returns>
  		/// <remarks>
  		/// If the <paramref name="classFullName"/> don't represent an <see cref="System.Type.AssemblyQualifiedName"/>
  		/// the method try to find the System.Type scanning all Assemblies of the <see cref="AppDomain.CurrentDomain"/>.
  		/// </remarks>
  		/// <exception cref="TypeLoadException">If no System.Type was found for <paramref name="classFullName"/>.</exception>
  				public static System.Type ClassForFullName(string classFullName)
  		{
  
gt: 			var result = ClassForFullNameOrNull(classFullName);