ProjectionAnalyzer.cs :  » 2.6.4-mono-.net-core » System.Data » System » Data » Services » Client » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » 2.6.4 mono .net core » System.Data 
System.Data » System » Data » Services » Client » ProjectionAnalyzer.cs
//Copyright 2010 Microsoft Corporation
//
//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
//You may obtain a copy of the License at 
//
//http://www.apache.org/licenses/LICENSE-2.0 
//
//Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 
//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
//See the License for the specific language governing permissions and limitations under the License.


namespace System.Data.Services.Client{
    #region Namespaces.

    using System;
    using System.Collections.ObjectModel;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Collections;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;

    #endregion Namespaces.

    internal static class ProjectionAnalyzer
    {
        #region Internal methods.

        internal static bool Analyze(LambdaExpression le, ResourceExpression re, bool matchMembers)
        {
            Debug.Assert(le != null, "le != null");

            if (le.Body.NodeType == ExpressionType.Constant)
            {
                if (ClientType.CheckElementTypeIsEntity(le.Body.Type))
                {
                    throw new NotSupportedException(Strings.ALinq_CannotCreateConstantEntity);
                }

                re.Projection = new ProjectionQueryOptionExpression(le.Body.Type, le, new List<string>());
                return true;
            }

            if (le.Body.NodeType == ExpressionType.MemberInit || le.Body.NodeType == ExpressionType.New)
            {
                AnalyzeResourceExpression(le, re);
                return true;
            }

            if (matchMembers)
            {
                Expression withoutConverts = SkipConverts(le.Body);
                if (withoutConverts.NodeType == ExpressionType.MemberAccess)
                {
                    AnalyzeResourceExpression(le, re);
                    return true;
                }
            }

            return false;
        }

        internal static void Analyze(LambdaExpression e, PathBox pb)
        {
            bool knownEntityType = ClientType.CheckElementTypeIsEntity(e.Body.Type);
            pb.PushParamExpression(e.Parameters.Last());

            if (!knownEntityType)
            {
                NonEntityProjectionAnalyzer.Analyze(e.Body, pb);
            }
            else
            {
                switch (e.Body.NodeType)
                {
                    case ExpressionType.MemberInit:
                        EntityProjectionAnalyzer.Analyze((MemberInitExpression)e.Body, pb);
                        break;
                    case ExpressionType.New:
                        throw new NotSupportedException(Strings.ALinq_CannotConstructKnownEntityTypes);
                    case ExpressionType.Constant:
                        throw new NotSupportedException(Strings.ALinq_CannotCreateConstantEntity);
                    default:
                        NonEntityProjectionAnalyzer.Analyze(e.Body, pb);
                        break;
                }
            }

            pb.PopParamExpression();
        }

        internal static bool IsMethodCallAllowedEntitySequence(MethodCallExpression call)
        {
            Debug.Assert(call != null, "call != null");
            return
                ReflectionUtil.IsSequenceMethod(call.Method, SequenceMethod.ToList) ||
                ReflectionUtil.IsSequenceMethod(call.Method, SequenceMethod.Select);
        }

        internal static void CheckChainedSequence(MethodCallExpression call, Type type)
        {
            if (ReflectionUtil.IsSequenceMethod(call.Method, SequenceMethod.Select))
            {
                MethodCallExpression insideCall = ResourceBinder.StripTo<MethodCallExpression>(call.Arguments[0]);
                if (insideCall != null && ReflectionUtil.IsSequenceMethod(insideCall.Method, SequenceMethod.Select))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(type, call.ToString()));
                }
            }
        }

        internal static bool IsCollectionProducingExpression(Expression e)
        {
            if (TypeSystem.FindIEnumerable(e.Type) != null)
            {
                Type elementType = TypeSystem.GetElementType(e.Type);
                Debug.Assert(elementType != null, "elementType == null");
                Type dscType = WebUtil.GetDataServiceCollectionOfT(elementType);
                if (typeof(List<>).MakeGenericType(elementType).IsAssignableFrom(e.Type) ||
                    (dscType != null && dscType.IsAssignableFrom(e.Type)))
                {
                    return true;
                }
            }

            return false;
        }

        internal static bool IsDisallowedExpressionForMethodCall(Expression e)
        {
            MemberExpression me = e as MemberExpression;
            if (me != null && ClientType.Create(me.Expression.Type, false).IsEntityType)
            {
                return false;
            }

            return IsCollectionProducingExpression(e);
        }


        #endregion Internal methods.

        #region Private methods.

        private static void Analyze(MemberInitExpression mie, PathBox pb)
        {
            Debug.Assert(mie != null, "mie != null");
            Debug.Assert(pb != null, "pb != null");

            bool knownEntityType = ClientType.CheckElementTypeIsEntity(mie.Type);
            if (knownEntityType)
            {
                EntityProjectionAnalyzer.Analyze(mie, pb);
            }
            else
            {
                NonEntityProjectionAnalyzer.Analyze(mie, pb);
            }
        }

        private static void AnalyzeResourceExpression(LambdaExpression lambda, ResourceExpression resource)
        {
            PathBox pb = new PathBox();
            ProjectionAnalyzer.Analyze(lambda, pb);
            resource.Projection = new ProjectionQueryOptionExpression(lambda.Body.Type, lambda, pb.ProjectionPaths.ToList());
            resource.ExpandPaths = pb.ExpandPaths.Union(resource.ExpandPaths, StringComparer.Ordinal).ToList();
        }

        private static Expression SkipConverts(Expression expression)
        {
            Expression result = expression;
            while (result.NodeType == ExpressionType.Convert || result.NodeType == ExpressionType.ConvertChecked)
            {
                result = ((UnaryExpression)result).Operand;
            }

            return result;
        }

        #endregion Private methods.

        #region Inner types.

        private class EntityProjectionAnalyzer : ExpressionVisitor
        {
            #region Private fields.

            private readonly PathBox box;

            private readonly Type type;

            #endregion Private fields.

            private EntityProjectionAnalyzer(PathBox pb, Type type)
            {
                Debug.Assert(pb != null, "pb != null");
                Debug.Assert(type != null, "type != null");
                
                this.box = pb;
                this.type = type;
            }

            internal static void Analyze(MemberInitExpression mie, PathBox pb)
            {
                Debug.Assert(mie != null, "mie != null");

                var epa = new EntityProjectionAnalyzer(pb, mie.Type);

                MemberAssignmentAnalysis targetEntityPath = null;
                foreach (MemberBinding mb in mie.Bindings)
                {
                    MemberAssignment ma = mb as MemberAssignment;
                    epa.Visit(ma.Expression);
                    if (ma != null)
                    {
                        var analysis = MemberAssignmentAnalysis.Analyze(pb.ParamExpressionInScope, ma.Expression);
                        if (analysis.IncompatibleAssignmentsException != null)
                        {
                            throw analysis.IncompatibleAssignmentsException;
                        }

                        Type targetType = GetMemberType(ma.Member);
                        Expression[] lastExpressions = analysis.GetExpressionsBeyondTargetEntity();
                        if (lastExpressions.Length == 0)
                        {
                            throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(targetType, ma.Expression));
                        }

                        MemberExpression lastExpression = lastExpressions[lastExpressions.Length - 1] as MemberExpression;
                        Debug.Assert(
                            !analysis.MultiplePathsFound, 
                            "!analysis.MultiplePathsFound -- the initilizer has been visited, and cannot be empty, and expressions that can combine paths should have thrown exception during initializer analysis");
                        Debug.Assert(
                            lastExpression != null,
                            "lastExpression != null -- the initilizer has been visited, and cannot be empty, and the only expressions that are allowed can be formed off the parameter, so this is always correlatd");
                        if (lastExpression != null && (lastExpression.Member.Name != ma.Member.Name))
                        {
                            throw new NotSupportedException(Strings.ALinq_PropertyNamesMustMatchInProjections(lastExpression.Member.Name, ma.Member.Name));
                        }

                        analysis.CheckCompatibleAssignments(mie.Type, ref targetEntityPath);

                        bool targetIsEntity = ClientType.CheckElementTypeIsEntity(targetType);
                        bool sourceIsEntity = ClientType.CheckElementTypeIsEntity(lastExpression.Type);
                        if (sourceIsEntity && !targetIsEntity)
                        {
                            throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(targetType, ma.Expression));
                        }
                    }
                }
            }

            internal override Expression VisitUnary(UnaryExpression u)
            {
                Debug.Assert(u != null, "u != null");

                if (ResourceBinder.PatternRules.MatchConvertToAssignable(u))
                {
                    return base.VisitUnary(u);
                }

                if ((u.NodeType == ExpressionType.Convert) || (u.NodeType == ExpressionType.ConvertChecked))
                {
                    Type sourceType = Nullable.GetUnderlyingType(u.Operand.Type) ?? u.Operand.Type;
                    Type targetType = Nullable.GetUnderlyingType(u.Type) ?? u.Type;

                    if (ClientConvert.IsKnownType(sourceType) && ClientConvert.IsKnownType(targetType))
                    {
                        return base.Visit(u.Operand);
                    }
                }

                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, u.ToString()));
            }

            internal override Expression VisitBinary(BinaryExpression b)
            {
                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, b.ToString()));
            }

            internal override Expression VisitTypeIs(TypeBinaryExpression b)
            {
                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, b.ToString()));
            }

            internal override Expression VisitConditional(ConditionalExpression c)
            {
                var nullCheck = ResourceBinder.PatternRules.MatchNullCheck(this.box.ParamExpressionInScope, c);
                if (nullCheck.Match)
                {
                    this.Visit(nullCheck.AssignExpression);
                    return c;
                }

                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, c.ToString()));
            }

            internal override Expression VisitConstant(ConstantExpression c)
            {
                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, c.ToString()));
            }

            internal override Expression VisitMemberAccess(MemberExpression m)
            {
                Debug.Assert(m != null, "m != null");

                if (!ClientType.CheckElementTypeIsEntity(m.Expression.Type) || IsCollectionProducingExpression(m.Expression))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, m.ToString()));
                }

                PropertyInfo pi = null;
                if (ResourceBinder.PatternRules.MatchNonPrivateReadableProperty(m, out pi))
                {
                    Expression e = base.VisitMemberAccess(m);
                    box.AppendToPath(pi);
                    return e;
                }

                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, m.ToString()));
            }

            internal override Expression VisitMethodCall(MethodCallExpression m)
            {
                if ((m.Object != null && IsDisallowedExpressionForMethodCall(m.Object))
                    || m.Arguments.Any(a => IsDisallowedExpressionForMethodCall(a)))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString()));
                }

                if (ProjectionAnalyzer.IsMethodCallAllowedEntitySequence(m))
                {
                    CheckChainedSequence(m, this.type);

                    return base.VisitMethodCall(m);
                }

                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, m.ToString()));
            }

            internal override Expression VisitInvocation(InvocationExpression iv)
            {
                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, iv.ToString()));
            }

            internal override Expression VisitLambda(LambdaExpression lambda)
            {
                ProjectionAnalyzer.Analyze(lambda, this.box);
                return lambda;
            }

            internal override Expression VisitListInit(ListInitExpression init)
            {
                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, init.ToString()));
            }

            internal override Expression VisitNewArray(NewArrayExpression na)
            {
                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, na.ToString()));
            }

            internal override Expression VisitMemberInit(MemberInitExpression init)
            {
                ProjectionAnalyzer.Analyze(init, this.box);
                return init;
            }

            internal override NewExpression VisitNew(NewExpression nex)
            {
                if (ResourceBinder.PatternRules.MatchNewDataServiceCollectionOfT(nex))
                {
                    if (ClientType.CheckElementTypeIsEntity(nex.Type))
                    {
                        foreach (Expression e in nex.Arguments)
                        {
                            if (e.NodeType != ExpressionType.Constant)
                            {
                                base.Visit(e);
                            }
                        }

                        return nex;
                    }
                }

                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, nex.ToString()));
            }

            internal override Expression VisitParameter(ParameterExpression p)
            {
                if (p != box.ParamExpressionInScope)
                {
                    throw new NotSupportedException(Strings.ALinq_CanOnlyProjectTheLeaf);
                }
                
                this.box.StartNewPath();
                return p;
            }

            private static Type GetMemberType(MemberInfo member)
            {
                Debug.Assert(member != null, "member != null");

                PropertyInfo propertyInfo = member as PropertyInfo;
                if (propertyInfo != null)
                {
                    return propertyInfo.PropertyType;
                }

                FieldInfo fieldInfo = member as FieldInfo;
                Debug.Assert(fieldInfo != null, "fieldInfo != null -- otherwise Expression.Member factory should have thrown an argument exception");
                return fieldInfo.FieldType;
            }
        }

        private class NonEntityProjectionAnalyzer : DataServiceExpressionVisitor
        {
            private PathBox box;

            private Type type;

            private NonEntityProjectionAnalyzer(PathBox pb, Type type)
            {
                this.box = pb;
                this.type = type;
            }

            internal static void Analyze(Expression e, PathBox pb)
            {
                var nepa = new NonEntityProjectionAnalyzer(pb, e.Type);

                MemberInitExpression mie = e as MemberInitExpression;

                if (mie != null)
                {
                    foreach (MemberBinding mb in mie.Bindings)
                    {
                        MemberAssignment ma = mb as MemberAssignment;
                        if (ma != null)
                        {
                            nepa.Visit(ma.Expression);
                        }
                    }
                }
                else
                {
                    nepa.Visit(e);
                }
            }

            internal override Expression VisitUnary(UnaryExpression u)
            {
                Debug.Assert(u != null, "u != null");

                if (!ResourceBinder.PatternRules.MatchConvertToAssignable(u))
                {
                    if (ClientType.CheckElementTypeIsEntity(u.Operand.Type))
                    {
                        throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, u.ToString()));
                    }
                }
                
                return base.VisitUnary(u);
            }

            internal override Expression VisitBinary(BinaryExpression b)
            {
                if (ClientType.CheckElementTypeIsEntity(b.Left.Type) || ClientType.CheckElementTypeIsEntity(b.Right.Type)
                    || IsCollectionProducingExpression(b.Left) || IsCollectionProducingExpression(b.Right))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, b.ToString()));
                }
                
                return base.VisitBinary(b);
            }

            internal override Expression VisitTypeIs(TypeBinaryExpression b)
            {
                if (ClientType.CheckElementTypeIsEntity(b.Expression.Type) || IsCollectionProducingExpression(b.Expression))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, b.ToString()));
                }
                
                return base.VisitTypeIs(b);
            }

            internal override Expression VisitConditional(ConditionalExpression c)
            {
                var nullCheck = ResourceBinder.PatternRules.MatchNullCheck(this.box.ParamExpressionInScope, c);
                if (nullCheck.Match)
                {
                    this.Visit(nullCheck.AssignExpression);
                    return c;
                }

                if (ClientType.CheckElementTypeIsEntity(c.Test.Type) || ClientType.CheckElementTypeIsEntity(c.IfTrue.Type) || ClientType.CheckElementTypeIsEntity(c.IfFalse.Type)
                    || IsCollectionProducingExpression(c.Test) || IsCollectionProducingExpression(c.IfTrue) || IsCollectionProducingExpression(c.IfFalse))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, c.ToString()));
                }
                
                return base.VisitConditional(c);
            }

            internal override Expression VisitMemberAccess(MemberExpression m)
            {
                Debug.Assert(m != null, "m != null");

                if (ClientConvert.IsKnownNullableType(m.Expression.Type))
                {
                    return base.VisitMemberAccess(m);
                }

                if (!ClientType.CheckElementTypeIsEntity(m.Expression.Type) || IsCollectionProducingExpression(m.Expression))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString()));
                }

                PropertyInfo pi = null;
                if (ResourceBinder.PatternRules.MatchNonPrivateReadableProperty(m, out pi))
                {
                    Expression e = base.VisitMemberAccess(m);
                    box.AppendToPath(pi);
                    return e;
                }

                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString()));
            }

            internal override Expression VisitMethodCall(MethodCallExpression m)
            {
                if ((m.Object != null && IsDisallowedExpressionForMethodCall(m.Object))
                    || m.Arguments.Any(a => IsDisallowedExpressionForMethodCall(a)))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString()));
                }

                if (ProjectionAnalyzer.IsMethodCallAllowedEntitySequence(m))
                {
                    CheckChainedSequence(m, this.type);

                    return base.VisitMethodCall(m);
                }

                if ((m.Object != null ? ClientType.CheckElementTypeIsEntity(m.Object.Type) : false) 
                    || m.Arguments.Any(a => ClientType.CheckElementTypeIsEntity(a.Type)))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString()));
                }

                return base.VisitMethodCall(m);
            }

            internal override Expression VisitInvocation(InvocationExpression iv)
            {
                if (ClientType.CheckElementTypeIsEntity(iv.Expression.Type) || IsCollectionProducingExpression(iv.Expression) 
                    || iv.Arguments.Any(a => ClientType.CheckElementTypeIsEntity(a.Type) || IsCollectionProducingExpression(a)))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, iv.ToString()));
                }

                return base.VisitInvocation(iv);
            }

            internal override Expression VisitLambda(LambdaExpression lambda)
            {
                ProjectionAnalyzer.Analyze(lambda, this.box);
                return lambda;
            }

            internal override Expression VisitMemberInit(MemberInitExpression init)
            {
                ProjectionAnalyzer.Analyze(init, this.box);
                return init;
            }

            internal override NewExpression VisitNew(NewExpression nex)
            {
                if (ClientType.CheckElementTypeIsEntity(nex.Type) &&
                    !ResourceBinder.PatternRules.MatchNewDataServiceCollectionOfT(nex))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, nex.ToString()));
                }

                return base.VisitNew(nex);
            }

            internal override Expression VisitParameter(ParameterExpression p)
            {
                if (p != box.ParamExpressionInScope)
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, p.ToString()));
                }

                this.box.StartNewPath();
                return p;
            }

            internal override Expression VisitConstant(ConstantExpression c)
            {
                if (ClientType.CheckElementTypeIsEntity(c.Type))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, c.ToString()));
                }

                return base.VisitConstant(c);
            }
        }

        #endregion Inner types.
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.