/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This software is subject to the Microsoft Public License (Ms-PL).
* A copy of the license can be found in the license.htm file included
* in this distribution.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
namespace System.Web.Mvc{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc.Resources;
public static class ExpressionHelper {
public static string GetExpressionText(string expression) {
return
String.Equals(expression, "model", StringComparison.OrdinalIgnoreCase)
? String.Empty // If it's exactly "model", then give them an empty string, to replicate the lambda behavior
: expression;
}
public static string GetExpressionText(LambdaExpression expression) {
// Crack the expression string for property/field accessors to create its name
Stack<string> nameParts = new Stack<string>();
Expression part = expression.Body;
while (part != null) {
if (part.NodeType == ExpressionType.Call) {
MethodCallExpression methodExpression = (MethodCallExpression)part;
if (!IsSingleArgumentIndexer(methodExpression)) {
break;
}
nameParts.Push(
GetIndexerInvocation(
methodExpression.Arguments.Single(),
expression.Parameters.ToArray()
)
);
part = methodExpression.Object;
}
else if (part.NodeType == ExpressionType.ArrayIndex) {
BinaryExpression binaryExpression = (BinaryExpression)part;
nameParts.Push(
GetIndexerInvocation(
binaryExpression.Right,
expression.Parameters.ToArray()
)
);
part = binaryExpression.Left;
}
else if (part.NodeType == ExpressionType.MemberAccess) {
MemberExpression memberExpressionPart = (MemberExpression)part;
nameParts.Push("." + memberExpressionPart.Member.Name);
part = memberExpressionPart.Expression;
}
else {
break;
}
}
// If it starts with "model", then strip that away
if (nameParts.Count > 0 && String.Equals(nameParts.Peek(), ".model", StringComparison.OrdinalIgnoreCase)) {
nameParts.Pop();
}
if (nameParts.Count > 0) {
return nameParts.Aggregate((left, right) => left + right).TrimStart('.');
}
return String.Empty;
}
private static string GetIndexerInvocation(Expression expression, ParameterExpression[] parameters) {
Expression converted = Expression.Convert(expression, typeof(object));
ParameterExpression fakeParameter = Expression.Parameter(typeof(object), null);
Expression<Func<object, object>> lambda = Expression.Lambda<Func<object, object>>(converted, fakeParameter);
Func<object, object> func;
try {
func = ExpressionUtil.CachedExpressionCompiler.Process(lambda);
}
catch (InvalidOperationException ex) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.ExpressionHelper_InvalidIndexerExpression,
expression,
parameters[0].Name
),
ex
);
}
return "[" + Convert.ToString(func(null), CultureInfo.InvariantCulture) + "]";
}
internal static bool IsSingleArgumentIndexer(Expression expression) {
MethodCallExpression methodExpression = expression as MethodCallExpression;
if (methodExpression == null || methodExpression.Arguments.Count != 1) {
return false;
}
return methodExpression.Method
.DeclaringType
.GetDefaultMembers()
.OfType<PropertyInfo>()
.Any(p => p.GetGetMethod() == methodExpression.Method);
}
}
}
|