Click here to Skip to main content
15,888,351 members
Articles / Programming Languages / C#

LINQ To Google Image and Google Groups

Rate me:
Please Sign up or sign in to vote.
5.00/5 (17 votes)
8 May 200710 min read 91.2K   400   48  
A LINQ Implementation for Google Images/Groups Search
using System;
using System.Collections.Generic;
using System.Text;

using System.Linq;
using System.Linq.Expressions;

namespace MChen.Linq.Google
{
    internal static class ImageQueryBuilder
    {
        public static ImageQueryInfo BuildQuery(Expression exp)
        {
            ImageQueryInfo qinfo = new ImageQueryInfo();
            return Visit(exp, qinfo);
        }

        private static ImageQueryInfo Visit(Expression node, ImageQueryInfo qinfo)
        {
            try
            {
                switch (node.NodeType)
                {
                    //expressions we will implement
                    case ExpressionType.AndAlso:
                        return VisitAnd((BinaryExpression)node, qinfo);

                    case ExpressionType.OrElse:
                        return VisitOr((BinaryExpression)node, qinfo);

                    case ExpressionType.Equal:
                        return VisitEquals((BinaryExpression)node, qinfo);

                    case ExpressionType.Call:
                        return VisitMethodCall((MethodCallExpression)node, qinfo, false, false);

                    case ExpressionType.Lambda:
                        return VisitLambda((LambdaExpression)node, qinfo);

                    case ExpressionType.Not:
                        return VisitNot((UnaryExpression)node, qinfo);

                    #region NOT IMPLEMENTED EXPRESSION TYPES
                    //exp types we don't intend to implement
                    case ExpressionType.MemberAccess:
                    case ExpressionType.Parameter:
                    case ExpressionType.Quote: //this is striped... Visit(((UnaryExpression)node).Operand, qinfo); break;
                    case ExpressionType.Constant:
                    case ExpressionType.Invoke:
                    case ExpressionType.ExclusiveOr:
                    case ExpressionType.And:
                    case ExpressionType.Or:
                    case ExpressionType.Add:
                    case ExpressionType.AddChecked:
                    case ExpressionType.Coalesce:
                    case ExpressionType.Divide:
                    case ExpressionType.GreaterThan:
                    case ExpressionType.GreaterThanOrEqual:
                    case ExpressionType.LessThan:
                    case ExpressionType.LessThanOrEqual:
                    case ExpressionType.Modulo:
                    case ExpressionType.Multiply:
                    case ExpressionType.MultiplyChecked:
                    case ExpressionType.NotEqual:
                    case ExpressionType.Subtract:
                    case ExpressionType.SubtractChecked:
                    case ExpressionType.ArrayLength:
                    case ExpressionType.Convert:
                    case ExpressionType.ConvertChecked:
                    case ExpressionType.Conditional:
                    case ExpressionType.Funclet:
                    case ExpressionType.LeftShift:
                    case ExpressionType.RightShift:
                    case ExpressionType.TypeAs:
                    case ExpressionType.TypeIs:
                    case ExpressionType.MemberInit:
                    case ExpressionType.Negate:
                    case ExpressionType.NegateChecked:
                    case ExpressionType.New:
                    case ExpressionType.NewArrayInit:
                    default:
                        throw new ArgumentException(
                              string.Format("Expression of type {0} not supported.", node.NodeType),
                              "exp");
                    #endregion
                }
            }
            finally
            {
            }
        }

        #region methods from DLINQ implementation
        private static Expression RemoveQuotes(Expression expression)
        {
            while (expression.NodeType == ExpressionType.Quote)
            {
                expression = ((UnaryExpression)expression).Operand;
            }
            return expression;
        }

        private static Expression RemoveConvert(Expression expression)
        {
            while (expression.NodeType == ExpressionType.Convert)
            {
                expression = ((UnaryExpression)expression).Operand;
            }
            return expression;
        }

        private static bool IsLambda(Expression expression)
        {
            return (RemoveQuotes(expression).NodeType == ExpressionType.Lambda);
        }

        private static bool IsMember(Expression expression)
        {
            return expression.NodeType == ExpressionType.MemberAccess ||
                (RemoveConvert(expression).NodeType == ExpressionType.MemberAccess);
        }

        private static LambdaExpression GetLambda(Expression expression)
        {
            return (RemoveQuotes(expression) as LambdaExpression);
        }

        private static MemberExpression GetMember(Expression expression)
        {
            if (expression.NodeType == ExpressionType.MemberAccess)
                return expression as MemberExpression;
            return (RemoveConvert(expression) as MemberExpression);
        }

        private static bool IsSequenceOperatorCall(MethodCallExpression mc)
        {
            Type declaringType = mc.Method.DeclaringType;
            if ((declaringType != typeof(Enumerable)) && (declaringType != typeof(Queryable)))
            {
                return false;
            }
            return true;
        }

        private static ImageQueryInfo VisitSequenceOperatorCall(MethodCallExpression mc, ImageQueryInfo qinfo)
        {
            Type declaringType = mc.Method.DeclaringType;
            //pass through the Constant expression
            if (mc.Arguments.Count == 0x2 && mc.Arguments[0].NodeType == ExpressionType.Constant)
            {
                ImageQuery<Image> qimg =
                    ((ConstantExpression)mc.Arguments[0]).Value as ImageQuery<Image>;
                if (qimg != null) qinfo = qimg._info;
            }

            //check "Where" and "OrderBy"
            switch (mc.Method.Name)
            {
                case "Where":
                    if (((mc.Arguments.Count != 0x2) || !IsLambda(mc.Arguments[0x1])) || (GetLambda(mc.Arguments[0x1]).Parameters.Count != 0x1))
                    {
                        break;
                    }
                    return VisitLambda(GetLambda(mc.Arguments[0x1]), qinfo); ;
                case "OrderBy":
                    if (((mc.Arguments.Count != 0x2) || !IsLambda(mc.Arguments[0x1])) || (GetLambda(mc.Arguments[0x1]).Parameters.Count != 0x1))
                    {
                        break;
                    }

                    //check order by
                    LambdaExpression lexp = GetLambda(mc.Arguments[1]);
                    if (IsMember(lexp.Body))
                    {
                        MemberExpression mexp = GetMember(lexp.Body);
                        if (mexp.Member.DeclaringType == typeof(Image) &&
                            mexp.Member.Name == "Rank") return qinfo;
                    }
                    throw new ArgumentException(
                              "Only order by Rank is supported.", "mc");
                default:
                    break;
            }
            throw new ArgumentException(
                string.Format("Sequence Call {0} not yet supported.", mc.Method.Name));
        }

        #endregion

        //visitors
        private static ImageQueryInfo VisitAnd(BinaryExpression node, ImageQueryInfo qinfo)
        {
            //dumb implementation for AND
            if (node.NodeType != ExpressionType.AndAlso)
                throw new ArgumentException("Argument is not AND.", "node");

            //simply visit left and right 
            qinfo = Visit(node.Left, qinfo);
            qinfo = Visit(node.Right, qinfo);
            return qinfo;
        }

        //visitors
        private static ImageQueryInfo VisitOr(BinaryExpression node, ImageQueryInfo qinfo)
        {
            //dumb implementation for OR, has to be on leaf nodes
            if (node.NodeType != ExpressionType.OrElse)
                throw new ArgumentException("Argument is not OR expression.", "node");

            //left leaf check
            if (node.Left.NodeType == ExpressionType.Call)
            {
                qinfo = VisitMethodCall((MethodCallExpression)node.Left, qinfo, true, false);
            }
            else if (node.Left.NodeType == ExpressionType.OrElse)
            {
                qinfo = VisitOr((BinaryExpression)node.Left, qinfo);
            }
            else
            {
                throw new ArgumentException("OR operator must be used on leaf expression nodes.", "node");
            }

            //simply visit left and right 
            if (node.Right.NodeType == ExpressionType.Call)
            {
                qinfo = VisitMethodCall((MethodCallExpression)node.Right, qinfo, true, false);
            }
            else if (node.Right.NodeType == ExpressionType.OrElse)
            {
                qinfo = VisitOr((BinaryExpression)node.Right, qinfo);
            }
            else
            {
                throw new ArgumentException("OR operator must be used on leaf expression nodes.", "node");
            }
            return qinfo;
        }

        private static ImageQueryInfo VisitNot(UnaryExpression node, ImageQueryInfo qinfo)
        {
            //only not over method call is supported!
            if (node.Operand.NodeType == ExpressionType.Call)
            {
                qinfo = VisitMethodCall((MethodCallExpression)node.Operand, qinfo, false, true);
            }
            else
            {
                throw new ArgumentException(
                    string.Format("Not operator on {0} not supported.", node.Operand.NodeType));
            }
            return qinfo;
        }

        private static ImageQueryInfo VisitEquals(BinaryExpression node, ImageQueryInfo qinfo)
        {
            //has to be memeber = constant or constant = member
            MemberExpression member = null;
            ConstantExpression constant = null;

            if (IsMember(node.Left)) member = GetMember(node.Left);
            else if (node.Left.NodeType == ExpressionType.Constant)
                constant = node.Left as ConstantExpression;
            if (IsMember(node.Right)) member = GetMember(node.Right);
            else if (node.Right.NodeType == ExpressionType.Constant)
                constant = node.Right as ConstantExpression;

            if (member == null || constant == null ||
                member.Member.DeclaringType != typeof(Image))
                throw new ArgumentException(
                      "Equals operator must apply to a Image member and a constant.");

            //only Domain, Type, Size and Color are supported
            switch (member.Member.Name)
            {
                case "Domain":
                    qinfo.Domain = constant.Value.ToString();
                    break;
                case "Format":
                    if (constant.Value.GetType() != typeof(Int32))
                        throw new ArgumentException("Image format is not valid.");
                    qinfo.Format = (ImageFormat)constant.Value;
                    break;
                case "Size":
                    if (constant.Value.GetType() != typeof(Int32))
                        throw new ArgumentException("Image size is not valid.");
                    qinfo.Size = (ImageSize)constant.Value;
                    break;
                case "Color":
                    if (constant.Value.GetType() != typeof(Int32))
                        throw new ArgumentException("Image color is not valid.");
                    qinfo.Color = (ImageColor)constant.Value;
                    break;
                default:
                    throw new ArgumentException(
                          "Only Size, Type, Color and Domain fields are supported for Equals operator.");
            }
            return qinfo;
        }

        //besides Where, only two function call: RelateTo && UnrelateTo are supported
        private static ImageQueryInfo VisitMethodCall(
            MethodCallExpression node, ImageQueryInfo qinfo, bool forceOr, bool forceNot)
        {
            Type declaringType = node.Method.DeclaringType;
            if (IsSequenceOperatorCall(node))
            {
                qinfo = VisitSequenceOperatorCall(node, qinfo);
            }
            else if (declaringType == typeof(Image))
            {
                if (node.Method.Name == "RelatesTo")
                {
                    //parse the parameter
                    if (node.Arguments.Count != 1 ||
                        node.Arguments[0].NodeType != ExpressionType.Constant)
                        throw new ArgumentException("Only constant search terms are supported.");

                    ConstantExpression cont = node.Arguments[0] as ConstantExpression;
                    string term = cont.Value.ToString();
                    if (forceNot) qinfo.NotWords.Add(term);
                    else if (forceOr) qinfo.OrWords.Add(term);
                    else qinfo.AllWords.Add(term);
                }
                else
                {
                    throw new ArgumentException(
                        string.Format("Method {0} is not supported.", node.Method.Name));
                }
            }
            else
            {
                throw new ArgumentException(
                      string.Format("Method {0} is not supported.", node.Method.Name));
            }
            return qinfo;
        }

        private static ImageQueryInfo VisitLambda(LambdaExpression node, ImageQueryInfo qinfo)
        {
            return Visit(node.Body, qinfo);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States

Comments and Discussions