<commit_before>using System;
using System.Linq.Expressions;
using NHibernate.Linq.Visitors;
using Remotion.Linq;
using Remotion.Linq.Clauses;
using Remotion.Linq.Clauses.Expressions;
using Remotion.Linq.Clauses.ResultOperators;

namespace NHibernate.Linq.GroupBy
{
	internal class GroupBySelectClauseRewriter : NhExpressionTreeVisitor
	{
		public static Expression ReWrite(Expression expression, GroupResultOperator groupBy, QueryModel model)
		{
			var visitor = new GroupBySelectClauseRewriter(groupBy, model);
			return visitor.VisitExpression(expression);
		}

		private readonly GroupResultOperator _groupBy;
		private readonly QueryModel _model;

		private GroupBySelectClauseRewriter(GroupResultOperator groupBy, QueryModel model)
		{
			_groupBy = groupBy;
			_model = model;
		}

		protected override Expression VisitQuerySourceReferenceExpression(QuerySourceReferenceExpression expression)
		{
			if (expression.ReferencedQuerySource == _groupBy)
			{
				return _groupBy.ElementSelector;
			}

			return base.VisitQuerySourceReferenceExpression(expression);
		}

		protected override Expression VisitMemberExpression(MemberExpression expression)
		{
			var parent = expression.Expression as MemberExpression;

			Expression result;
			if (parent != null &&
				parent.Member.Name == "Key" &&
				IsMemberOfModel(parent) &&
				TryFindTransparentPropertyOfAnonymousObject(_groupBy.KeySelector, expression.Member.Name, out result))
			{
				return result;
			}

			if (!IsMemberOfModel(expression))
			{
				return base.VisitMemberExpression(expression);
			}

			if (expression.Member.Name == "Key")
			{
				return _groupBy.KeySelector;
			}

			var elementSelector = _groupBy.ElementSelector;

			if ((elementSelector is MemberExpression) || (elementSelector is QuerySourceReferenceExpression))
			{
				// If ElementSelector is MemberExpression, just return
				return base.VisitMemberExpression(expression);
			}

			// If ElementSelector is NewExpression, then search for member of name "get_" + originalMemberExpression.Member.Name
			if (TryFindTransparentPropertyOfAnonymousObject(elementSelector, expression.Member.Name, out result))
				return result;

			throw new NotImplementedException();
		}

		private static bool TryFindTransparentPropertyOfAnonymousObject(Expression expression, string memberName, out Expression result)
		{
			// This method bypasses usage of anonymous object properties.
			// For ex. `new { a = SomeProperty, b = OtherProperty, c = OneMoreProperty }.a` would be transformed to
			// `SomeProperty`
			// TODO - this wouldn't handle nested initialisers.  Should do a tree walk to find the correct member
			var newExpression = expression as NewExpression;
			if (newExpression != null)
			{
				var i = 0;
				foreach (var member in newExpression.Members)
				{
					if (member.Name == "get_" + memberName)
					{
						result = newExpression.Arguments[i];
						return true;
					}
					i++;
				}
			}
			result = null;
			return false;
		}

		// TODO - dislike this code intensly.  Should probably be a tree-walk in its own right
		private bool IsMemberOfModel(MemberExpression expression)<commit_msg>Fix GroupBySelectClauseRewriter.cs for .Net 4.0 (NH-3256).<commit_after>using System;
using System.Linq.Expressions;
using NHibernate.Linq.Visitors;
using Remotion.Linq;
using Remotion.Linq.Clauses;
using Remotion.Linq.Clauses.Expressions;
using Remotion.Linq.Clauses.ResultOperators;

namespace NHibernate.Linq.GroupBy
{
	internal class GroupBySelectClauseRewriter : NhExpressionTreeVisitor
	{
		public static Expression ReWrite(Expression expression, GroupResultOperator groupBy, QueryModel model)
		{
			var visitor = new GroupBySelectClauseRewriter(groupBy, model);
			return visitor.VisitExpression(expression);
		}

		private readonly GroupResultOperator _groupBy;
		private readonly QueryModel _model;

		private GroupBySelectClauseRewriter(GroupResultOperator groupBy, QueryModel model)
		{
			_groupBy = groupBy;
			_model = model;
		}

		protected override Expression VisitQuerySourceReferenceExpression(QuerySourceReferenceExpression expression)
		{
			if (expression.ReferencedQuerySource == _groupBy)
			{
				return _groupBy.ElementSelector;
			}

			return base.VisitQuerySourceReferenceExpression(expression);
		}

		protected override Expression VisitMemberExpression(MemberExpression expression)
		{
			var parent = expression.Expression as MemberExpression;

			Expression result;
			if (parent != null &&
				parent.Member.Name == "Key" &&
				IsMemberOfModel(parent) &&
				TryFindTransparentPropertyOfAnonymousObject(_groupBy.KeySelector, expression.Member.Name, out result))
			{
				return result;
			}

			if (!IsMemberOfModel(expression))
			{
				return base.VisitMemberExpression(expression);
			}

			if (expression.Member.Name == "Key")
			{
				return _groupBy.KeySelector;
			}

			var elementSelector = _groupBy.ElementSelector;

			if ((elementSelector is MemberExpression) || (elementSelector is QuerySourceReferenceExpression))
			{
				// If ElementSelector is MemberExpression, just return
				return base.VisitMemberExpression(expression);
			}

			// If ElementSelector is NewExpression, then search for member of name "get_" + originalMemberExpression.Member.Name
			if (TryFindTransparentPropertyOfAnonymousObject(elementSelector, expression.Member.Name, out result))
				return result;

			throw new NotImplementedException();
		}

		private static bool TryFindTransparentPropertyOfAnonymousObject(Expression expression, string memberName, out Expression result)
		{
			// This method bypasses usage of anonymous object properties.
			// For ex. `new { a = SomeProperty, b = OtherProperty, c = OneMoreProperty }.a` would be transformed to
			// `SomeProperty`
			// TODO - this wouldn't handle nested initialisers.  Should do a tree walk to find the correct member
			var newExpression = expression as NewExpression;
			if (newExpression != null)
			{
				var i = 0;
				foreach (var member in newExpression.Members)
				{
					// .Net 2.0: The member will be a MethodInfo named in the get_XXX pattern.
					// .Net 4.0: The member will be a PropertyInfo (i.e. without name prefix).
					// re-linq seem to have a more advanced matching implemented in
					// its TransparentIdentifierRemovingExpressionTreeVisitor.
					var altMemberName = "get_" + memberName;
					if (member.Name == memberName || member.Name == altMemberName)
					{
						result = newExpression.Arguments[i];
						return true;
					}
					i++;
				}
			}
			result = null;
			return false;
		}

		// TODO - dislike this code intensly.  Should probably be a tree-walk in its own right
		private bool IsMemberOfModel(MemberExpression expression)
{'fraction_non_alphanumeric': 0.07840214637054703, 'fraction_numerical': 0.0017886421225219854, 'mean_word_length': 4.075642965204236, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2558061', 'n_tokens_mistral': 2181, 'n_tokens_neox': 1782, 'n_words': 514}