/*
Kooboo is a content management system based on ASP.NET MVC framework. Copyright 2009 Yardi Technology Limited.
This program is free software: you can redistribute it and/or modify it under the terms of the
GNU General Public License version 3 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program.
If not, see http://www.kooboo.com/gpl3/.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Everest.Library.LINQ;
using System.Linq.Expressions;
using System.Reflection;
namespace Everest.CmsServices.LINQ{
internal class ContentQueryTranslator : ExpressionVisitor
{
const string Param = "ContentParam{0}";
//StringBuilder queryStatement = new StringBuilder();
IDictionary<string, object> _parameters = new Dictionary<string, object>();
internal virtual StatementExpression Translate(Expression expression)
{
return (StatementExpression)this.Visit(expression);
}
//public string QueryStatement
//{
// get
// {
// return this.queryStatement.ToString();
// }
//}
//public IDictionary<string, object> Parameters
//{
// get
// {
// return parameters;
// }
//}
protected override Expression Visit(Expression exp)
{
if (exp == null)
return exp;
switch ((ContentExpressionType)exp.NodeType)
{
case ContentExpressionType.Statement:
return exp;
default:
return base.Visit(exp);
}
}
private bool IsGetFieldMethod(MethodCallExpression m)
{
if (m.Object == null)
{
return false;
}
return typeof(ContentContainer).IsAssignableFrom(m.Object.Type) && m.Method.Name == "GetColumn";
}
protected override Expression VisitBinary(BinaryExpression b)
{
Expression left = this.Visit(b.Left);
Expression right = this.Visit(b.Right);
Expression conversion = this.Visit(b.Conversion);
if (left is ColumnExpression && right is ColumnExpression)
{
ColumnExpression leftExpression = (ColumnExpression)left;
ColumnExpression rightExpression = (ColumnExpression)right;
var queryStatement = string.Format("{0} {1} {2}", leftExpression.ColumnName, GetOperator(b.NodeType), rightExpression.ColumnName);
return new StatementExpression(queryStatement, _parameters);
}
if (left is ColumnExpression || right is ColumnExpression)
{
ColumnExpression columnExpression = null;
Expression valueExpression = null;
if (left is ColumnExpression)
{
columnExpression = (ColumnExpression)left;
valueExpression = right;
}
if (right is ColumnExpression)
{
columnExpression = (ColumnExpression)right;
valueExpression = left;
}
//Expression obj = this.Visit(m.Object);
//IEnumerable<Expression> args = this.VisitExpressionList(m.Arguments);
var fieldValue = GetFieldValue(valueExpression);
var columnName = columnExpression.ColumnName.ToString();
var paramName = GetParamName();
var queryStatement = string.Format("{0} {1} {{{2}}}", columnName, GetOperator(b.NodeType), paramName);
_parameters.Add(paramName, fieldValue);
return new StatementExpression(queryStatement, _parameters);
}
if (left is StatementExpression || right is StatementExpression)
{
var lefStatement = ConvertToStatementExpression(left);
var rightStatement = ConvertToStatementExpression(right);
var queryStatement = string.Format("({0} {1} {2})", lefStatement.QueryStatement, GetOperator(b.NodeType), rightStatement.QueryStatement);
return new StatementExpression(queryStatement, _parameters);
}
return PartialEvaluator.Eval(b);
}
private StatementExpression ConvertToStatementExpression(Expression exp)
{
if (exp is StatementExpression)
{
return (StatementExpression)exp;
}
var value = (ConstantExpression)PartialEvaluator.Eval(exp);
return new StatementExpression((bool)value.Value, null);
}
private string GetParamName()
{
var paramName = string.Format(Param, _parameters.Count);
return paramName;
}
private object GetFieldValue(Expression fieldValueExpression)
{
var constant = (ConstantExpression)PartialEvaluator.Eval(fieldValueExpression);
if (constant.Value is bool)
{
return Convert.ToInt32(constant.Value);
}
return constant.Value;
}
private string GetOperator(ExpressionType type)
{
switch (type)
{
case ExpressionType.Equal:
return "=";
case ExpressionType.AndAlso:
return "AND";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
case ExpressionType.NotEqual:
return "<>";
case ExpressionType.OrElse:
return "OR";
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.And:
case ExpressionType.ArrayIndex:
case ExpressionType.ArrayLength:
case ExpressionType.Call:
case ExpressionType.Coalesce:
case ExpressionType.Conditional:
case ExpressionType.Constant:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.Divide:
case ExpressionType.ExclusiveOr:
case ExpressionType.Invoke:
case ExpressionType.Lambda:
case ExpressionType.LeftShift:
case ExpressionType.ListInit:
case ExpressionType.MemberAccess:
case ExpressionType.MemberInit:
case ExpressionType.Modulo:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.New:
case ExpressionType.NewArrayBounds:
case ExpressionType.NewArrayInit:
case ExpressionType.Not:
case ExpressionType.Or:
case ExpressionType.Parameter:
case ExpressionType.Power:
case ExpressionType.Quote:
case ExpressionType.RightShift:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
case ExpressionType.TypeAs:
case ExpressionType.TypeIs:
case ExpressionType.UnaryPlus:
default:
throw new NotSupportedException();
}
}
protected override Expression VisitMethodCall(MethodCallExpression m)
{
Expression obj = this.Visit(m.Object);
IEnumerable<Expression> args = this.VisitExpressionList(m.Arguments);
if (IsGetFieldMethod(m))
{
var fieldName = (ConstantExpression)PartialEvaluator.Eval(args.First());
if (fieldName == null || string.IsNullOrEmpty(fieldName.ToString()))
{
throw new ArgumentNullException("fieldName");
}
return new ColumnExpression(fieldName.ToString());
}
if (obj is ColumnExpression)
{
ColumnExpression getColumn = (ColumnExpression)obj;
var paramName = GetParamName();
object fieldValue = null;
string queryStatement;
switch (m.Method.Name)
{
case "Contains":
fieldValue = GetFieldValue(args.First());
queryStatement = string.Format("{0} LIKE '%' + {{{1}}} + '%'", getColumn.ColumnName, paramName);
_parameters.Add(paramName, fieldValue);
return new StatementExpression(queryStatement, _parameters);
case "Equals":
fieldValue = GetFieldValue(args.First());
queryStatement = string.Format("{0} {1} {{{2}}}", getColumn.ColumnName, GetOperator(ExpressionType.Equal), paramName);
_parameters.Add(paramName, fieldValue);
return new StatementExpression(queryStatement, _parameters);
default:
throw new NotSupportedException(string.Format("Not supported method: {0}", m.Method.Name));
}
}
if (args.First() is StatementExpression)
{
StatementExpression parameter1 = (StatementExpression)args.First();
switch (m.Method.Name)
{
case "Where":
StatementExpression parameter2 = (StatementExpression)args.Last();
var queryStatement = string.Format("({0} {1} {2})", parameter1.QueryStatement, GetOperator(ExpressionType.AndAlso), parameter2.QueryStatement);
return new StatementExpression(queryStatement, _parameters);
case "Skip":
ConstantExpression skipCount = (ConstantExpression)args.Last();
parameter1.SkipCount += (int)skipCount.Value;
return parameter1;
case "OrderBy":
ColumnExpression column = (ColumnExpression)args.Last();
parameter1.OrderBy += string.Format(" {0} ASC", column.ColumnName);
return parameter1;
case "OrderByDescending":
ColumnExpression column1 = (ColumnExpression)args.Last();
parameter1.OrderBy += string.Format(" {0} DESC", column1.ColumnName);
return parameter1;
case "Take":
ConstantExpression takeCount = (ConstantExpression)args.Last();
parameter1.TakeCount = (int)takeCount.Value;
return parameter1;
case "Count":
case "First":
case "FirstOrDefault":
case "Last":
case "LastOrDefault":
SupportedQueryType queryType = (SupportedQueryType)Enum.Parse(typeof(SupportedQueryType), m.Method.Name);
parameter1.QueryMethodType = queryType;
parameter1.MethodParameters = args.Skip(1);
return parameter1;
default:
throw new NotSupportedException(m.Method.Name);
}
}
return PartialEvaluator.Eval(m);
}
protected override Expression VisitLambda(LambdaExpression lambda)
{
Expression body = this.Visit(lambda.Body);
if (body is ConstantExpression)
{
return new StatementExpression((bool)((ConstantExpression)body).Value, null);
}
return body;
//if (body is StatementExpression)
//{
// return body;
//}
//if (body != lambda.Body)
//{
// return
//}
//return lambda;
}
protected override Expression VisitUnary(UnaryExpression u)
{
Expression operand = this.Visit(u.Operand);
return operand;
}
}
}
|