/// <summary>
 /// Initializes a new instance of the FunctionSegment class
 /// </summary>
 /// <param name="function">the function representing the Function</param>
 internal FunctionSegment(Function function)
     : base()
 {
     ExceptionUtilities.CheckArgumentNotNull(function, "function");
     this.Container = null;
     this.Function = function;
     this.UseParentheses = false;
 }
 /// <summary>
 /// Initializes a new instance of the FunctionSegment class
 /// </summary>
 /// <param name="function">the function representing the Function</param>
 /// <param name="container">the entity container for the function</param>
 /// <param name="useParentheses">used to determine if the uri generated will have parentheses</param>
 internal FunctionSegment(Function function, EntityContainer container, bool useParentheses)
     : base()
 {
     ExceptionUtilities.CheckArgumentNotNull(function, "function");
     this.Container = container;
     this.Function = function;
     this.UseParentheses = useParentheses;
 }
        /// <summary>
        /// Checks for a name collision between an action's name and a property's name
        /// </summary>
        /// <param name="function">The function to check for name property collision</param>
        /// <param name="bindingEntityDataType">The bindingEntityDataType is the entity to check against</param>
        /// <param name="actualInstanceEntityType">the entity type of the instance of the entity</param>
        /// <returns>a string representing the entity container's name</returns>
        public string BuildExpectedContainerName(Function function, EntityDataType bindingEntityDataType, EntityType actualInstanceEntityType)
        {
            string containerName = string.Empty;

            // check for open entity type on the function definition or on the actual entity itself
            if (bindingEntityDataType.Definition.IsOpen || actualInstanceEntityType.IsOpen)
            {
                containerName = string.Concat(function.Model.GetDefaultEntityContainer().Name, ".");
            }

            var foundNameCollision = bindingEntityDataType.Definition.AllProperties.Where(a => a.Name.Equals(function.Name)).FirstOrDefault();
            if (foundNameCollision != null && !bindingEntityDataType.Definition.IsOpen)
            {
                containerName = string.Concat(function.Model.GetDefaultEntityContainer().Name, ".");
            }

            return containerName;
        }
        /// <summary>
        /// Converts a Payload that contains the action parameters into QueryValues
        /// </summary>
        /// <param name="parametersPayload">Parameter Payload</param>
        /// <param name="action">Function that has the parameters that will be converted to a QueryValue</param>
        /// <returns>A Lookup of parameter names and QueryValues</returns>
        public IDictionary<string, QueryValue> Convert(ComplexInstance parametersPayload, Function action)
        {
            var parametersLookup = new Dictionary<string, QueryValue>();

            foreach (var property in parametersPayload.Properties)
            {
                var functionParameter = action.Parameters.Single(p => p.Name == property.Name);
                var functionParameterQueryType = this.QueryTypeLibrary.GetDefaultQueryType(functionParameter.DataType);
                QueryValue parameterValue = null;
                if (property.ElementType == ODataPayloadElementType.ComplexMultiValueProperty)
                {
                    var complexMultValueProperty = property as ComplexMultiValueProperty;
                    parameterValue = this.PayloadElementToQueryValueConverter.Convert(complexMultValueProperty.Value, functionParameterQueryType);
                }
                else if (property.ElementType == ODataPayloadElementType.ComplexProperty)
                {
                    var complexProperty = property as ComplexProperty;
                    parameterValue = this.PayloadElementToQueryValueConverter.Convert(complexProperty.Value, functionParameterQueryType);
                }
                else if (property.ElementType == ODataPayloadElementType.PrimitiveMultiValueProperty)
                {
                    var derivedProperty = property as PrimitiveMultiValueProperty;
                    parameterValue = this.PayloadElementToQueryValueConverter.Convert(derivedProperty.Value, functionParameterQueryType);
                }
                else if (property.ElementType == ODataPayloadElementType.PrimitiveProperty)
                {
                    var derivedProperty = property as PrimitiveProperty;
                    parameterValue = this.PayloadElementToQueryValueConverter.Convert(derivedProperty.Value, functionParameterQueryType);
                }

                ExceptionUtilities.CheckObjectNotNull(parameterValue, "Cannot convert to query value parameter for Action that is of type {0} and has a payload like {1}", property.ElementType, property);

                parametersLookup.Add(property.Name, parameterValue);
            }

            return parametersLookup;
        }
 /// <summary>
 /// Adds new <see cref="Function"/> to the model.
 /// </summary>
 /// <param name="function">Function to be added</param>
 public void Add(Function function)
 {
     ExceptionUtilities.CheckArgumentNotNull(function, "function");
     ExceptionUtilities.Assert(function.Model == null, "Function was already added to another model");
     function.Model = this;
     this.functionsList.Add(function);
 }
        /// <summary>
        /// Find out whether action matches with the ODataUri select function segment
        /// </summary>
        /// <param name="action">The action</param>
        /// <param name="selectSegment">The segment</param>
        /// <returns>Whether action matches with the function segment</returns>
        private bool FuctionMatchWithSelectFunctionSegment(Function action, ODataUriSegment selectSegment)
        {
            FunctionSegment selectFunctionSegment = selectSegment as FunctionSegment;
            if (selectFunctionSegment != null && selectFunctionSegment.Function == action)
            {
                return true;
            }

            // Sometimes the $select segment is an UnrecognizedSegment
            UnrecognizedSegment otherTypeOfSelectSegment = selectSegment as UnrecognizedSegment;
            if (otherTypeOfSelectSegment != null && selectFunctionSegment == null)
            {
                string actionName = action.Name;

                // Determine the if selction segment was <Action Name> or <EntityContainer>.<Action Name>
                if (otherTypeOfSelectSegment.Value.Contains('.'))
                {
                    actionName = string.Concat(action.Model.GetDefaultEntityContainer().Name, ".", actionName);    
                }

                if (otherTypeOfSelectSegment.Value.Equals(actionName, System.StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }
            }

            return false;
        }
        protected virtual void VisitFunction(Function function)
        {
            this.VisitAnnotatedItem(function);

            foreach (var parameter in function.Parameters)
            {
                this.VisitFunctionParameter(parameter);
            }
        }
        /// <summary>
        /// Find out whether to expect action descriptor with projection in request uri
        /// </summary>
        /// <param name="requestUri">The request uri</param>
        /// <param name="action">The action</param>
        /// <param name="isTopLevelElement">Whether the entity being verified is top level payload element</param>
        /// <returns>Whether to expect action descriptor</returns>
        private bool ExpectActionWithProjection(ODataUri requestUri, Function action, bool isTopLevelElement)
        {
            ODataUriSegmentPathCollection selectSegments = requestUri.SelectSegments;
            ODataUriSegmentPathCollection expandSegments = requestUri.ExpandSegments;

            // handle single level $select path, expect action descriptor if $select=ActionName or $select=Container.*
            foreach (var selectSegmentPath in selectSegments.Where(ss => ss.Count == 1))
            {
                ODataUriSegment selectSegment = selectSegmentPath.Single();

                if (isTopLevelElement && this.FuctionMatchWithSelectFunctionSegment(action, selectSegment))
                {
                    return true;
                }

                if (this.IsSelectAllFunctionSegment(selectSegment))
                {
                    return true;
                }
            }

            // handle multiple level $select path, expect action descriptor for $expand=Rating if: $select=Rating or $select=Rating/ActionName or $Select=Rating/Container.*
            foreach (var expandSegmentPath in expandSegments)
            {
                List<ODataUriSegment> expandSegmentList = expandSegmentPath.ToList();
                foreach (var selectSegmentPath in selectSegments.Where(ss => ss.Count == expandSegmentPath.Count || ss.Count == expandSegmentPath.Count + 1))
                {
                    List<ODataUriSegment> selectSegmentList = selectSegmentPath.ToList();
                    if (this.FunctionMatchWithExpandSegmentList(selectSegmentList, expandSegmentList, action))
                    {
                        return true;
                    }
                }
            }

            return false;
        }
        /// <summary>
        /// Find out whether to expect action descriptor given expand and select segments.
        /// </summary>
        /// <param name="selectSegmentList">The select segments</param>
        /// <param name="expandSegmentList">The expand segments</param>
        /// <param name="action">The action</param>
        /// <returns>Whether to expect action descriptor</returns>
        private bool FunctionMatchWithExpandSegmentList(List<ODataUriSegment> selectSegmentList, List<ODataUriSegment> expandSegmentList, Function action)
        {
            // check if $select path and $expand path matchs
            int index = 0;
            NavigationSegment expandNavSegment = null;
            foreach (var expandSegment in expandSegmentList)
            {
                expandNavSegment = expandSegment as NavigationSegment;
                ExceptionUtilities.CheckArgumentNotNull(expandNavSegment, "Unexpected expand segment.");
                NavigationSegment selectNavSegment = selectSegmentList[index] as NavigationSegment;
                if (selectNavSegment == null || !expandNavSegment.NavigationProperty.Equals(selectNavSegment.NavigationProperty))
                {
                    return false;
                }

                index++;
            }

            // check if the action binding type matches with last segment of $expand path
            EntityDataType bindingEntityDataType = action.Parameters.First().DataType as EntityDataType;
            ExceptionUtilities.CheckArgumentNotNull(bindingEntityDataType, "Unexpected feed-bound action.");
            EntityType bindingEntityType = bindingEntityDataType.Definition;
            if (bindingEntityType != expandNavSegment.NavigationProperty.ToAssociationEnd.EntityType)
            {
                return false;
            }

            // expect action descriptor (return true) for $expand=Rating if: $select=Rating, $select=Rating/ActionName, $Select=Rating/Container.*
            if (selectSegmentList.Count == expandSegmentList.Count + 1)
            {
                ODataUriSegment lastSelectSegment = selectSegmentList.Last();
                return this.FuctionMatchWithSelectFunctionSegment(action, lastSelectSegment) || this.IsSelectAllFunctionSegment(lastSelectSegment);
            }

            return true;
        }
        private bool FunctionSignaturesAreSame(Function f1, Function f2)
        {
            if (f1.FullName != f2.FullName)
            {
                return false;
            }

            return this.ParametersMatch(f1.Parameters, f2.Parameters);
        }
        /// <summary>
        /// Checks if the expected action descriptor in the case where the entity type is open and the property is open
        /// </summary>
        /// <param name="function">The function to check for name property collision</param>
        /// <param name="bindingEntityDataType">The bindingEntityDataType is the entity to check against</param>
        /// <param name="expectActionDescriptor">current value of the expected behavior</param>
        /// <returns>true is the action descriptor is expected in response payload</returns>
        public bool VerifyIfOpenType(Function function, EntityDataType bindingEntityDataType, bool expectActionDescriptor)
        {
            if ((expectActionDescriptor == false) && bindingEntityDataType.Definition.IsOpen)
            {
                // in open types, an action with a name collision will be returned in metadata
                var possibleNameCollision = bindingEntityDataType.Definition.AllProperties.Where(a => a.Name.Equals(function.Name)).FirstOrDefault();
                if (possibleNameCollision != null)
                {
                    // if property is not declared, then action descriptor will be present
                    expectActionDescriptor = !possibleNameCollision.IsMetadataDeclaredProperty();
                }
            }

            return expectActionDescriptor;
        }
 /// <summary>
 /// Initializes a new instance of the LinqToAstoriaReplaceFunctionParameterReferenceVisitor class
 /// </summary>
 /// <param name="function">The function for which to replace parameters</param>
 /// <param name="arguments">Argumnets for the function call</param>
 public LinqToAstoriaReplaceFunctionParameterReferenceVisitor(Function function, IEnumerable<QueryExpression> arguments)
 {
     this.PushFunctionCallToStack(function, arguments);
 }
        private void CompareFunction(Function expectedFunction, Function actualFunction)
        {
            this.CompareReturnType(
                expectedFunction.ReturnType,
                actualFunction.ReturnType,
                string.Format(CultureInfo.InvariantCulture, "Function '{0}'", expectedFunction.Name));

            this.CompareParameters(
                expectedFunction.Parameters,
                actualFunction.Parameters,
                string.Format(CultureInfo.InvariantCulture, "Function '{0}'", expectedFunction.Name));
        }
        private StoredProcedure ConvertToStoredProcedure(Function customFunction)
        {
            string functionName = this.GetFunctionName(customFunction);
            string schema = this.GetSchemaName(customFunction);

            var storedProcedure = new StoredProcedure(schema, functionName);
            foreach (var parameter in customFunction.Parameters)
            {
                storedProcedure.Parameters.Add(this.ConvertToDatabaseFunctionParameter(parameter));
            }

            var bodyAnnotation = customFunction.Annotations.OfType<StoreFunctionBodyAnnotation>().SingleOrDefault();
            if (bodyAnnotation != null)
            {
                storedProcedure.Body = bodyAnnotation.Body;
            }

            foreach (var returnTypeAnnotation in customFunction.Annotations.OfType<StoredProcedureReturnTypeFunctionAnnotation>())
            {
                var storedProcedureReturnTypeAnnotation = new StoredProcedureReturnTypeAnnotation(this.ConvertToDatabaseType(returnTypeAnnotation.ReturnType));
                storedProcedureReturnTypeAnnotation.BodyGenerationAnnotation = this.GetStoreFunctionBodyGenerationInfoToStoreItem(returnTypeAnnotation.BodyGenerationAnnotation);
                storedProcedure.Annotations.Add(storedProcedureReturnTypeAnnotation);
            }

            return storedProcedure;
        }
 /// <summary>
 /// Removes a <see cref="Function"/> from the model.
 /// </summary>
 /// <param name="function">Function to be removed</param>
 public void Remove(Function function)
 {
     ExceptionUtilities.CheckArgumentNotNull(function, "function");
     ExceptionUtilities.Assert(function.Model == this, "Function was not added to this model");
     ExceptionUtilities.Assert(this.functionsList.Remove(function), "Function was not added to this model");
     function.Model = null;
 }
 private void AddCommandTextToAnnotation(Function function, string body)
 {
     var metatdataAnnotation = function.Annotations.OfType<StoreFunctionMetadataAnnotation>().Single();
     metatdataAnnotation.CommandText = body;
 }
        private bool ShouldGenerateCommandText(Function function)
        {
            var useCommandTextAnnotation = function.Annotations.OfType<UseStoreFunctionCommandTextAnnotation>().SingleOrDefault();
            if (useCommandTextAnnotation != null)
            {
                return useCommandTextAnnotation.UseCommandText;
            }

            return false;
        }
        private string GetSchemaName(Function customFunction)
        {
            string schema = null;

            var functionInStoreAnnotation = customFunction.Annotations.OfType<StoreFunctionMetadataAnnotation>().SingleOrDefault();
            if (functionInStoreAnnotation != null)
            {
                schema = string.IsNullOrEmpty(functionInStoreAnnotation.Schema) ? null : functionInStoreAnnotation.Schema;
            }

            return schema;
        }
        private string GetFunctionName(Function customFunction)
        {
            string functionName = customFunction.Name;

            var functionInStoreAnnotation = customFunction.Annotations.OfType<StoreFunctionMetadataAnnotation>().SingleOrDefault();
            if (functionInStoreAnnotation != null)
            {
                functionName = string.IsNullOrEmpty(functionInStoreAnnotation.StoreFunctionName) ? functionName : functionInStoreAnnotation.StoreFunctionName;
            }

            return functionName;
        }
        private DatabaseFunction ConvertToDatabaseFunction(Function customFunction)
        {
            string functionName = this.GetFunctionName(customFunction);
            string schema = this.GetSchemaName(customFunction);

            var databaseFunction = new DatabaseFunction(schema, functionName);
            databaseFunction.ReturnType = this.ConvertToDatabaseType(customFunction.ReturnType);

            foreach (var parameter in customFunction.Parameters)
            {
                databaseFunction.Parameters.Add(this.ConvertToDatabaseFunctionParameter(parameter));
            }

            var bodyGenerationAnnotation = this.GetStoreFunctionBodyGenerationInfoToStoreItem(customFunction.Annotations.OfType<StoreFunctionBodyGenerationInfoAnnotation>().SingleOrDefault());
            if (bodyGenerationAnnotation != null)
            {
                databaseFunction.Annotations.Add(bodyGenerationAnnotation);
            }

            var bodyAnnotation = customFunction.Annotations.OfType<StoreFunctionBodyAnnotation>().SingleOrDefault();
            if (bodyAnnotation != null)
            {
                databaseFunction.Body = bodyAnnotation.Body;
            }

            return databaseFunction;
        }
        /// <summary>
        /// Creates a Function that takes one or more arguments, and returns the first of its arguments
        /// </summary>
        /// <param name="name">
        /// The name of the function 
        /// </param>
        /// <param name="firstArgumentType">The type of the first argument for the function</param>
        /// <param name="otherArgumentsTypes">The types of the rest of the arguments for the function</param>
        /// <returns> A Function that returns the first of its parameters.</returns>
        public static Function CreateSimpleServiceOperation(string name, DataType firstArgumentType, params DataType[] otherArgumentsTypes)
        {
            ExceptionUtilities.CheckArgumentNotNull(name, "name");
            ExceptionUtilities.CheckArgumentNotNull(firstArgumentType, "firstArgumentType");
            ExceptionUtilities.CheckArgumentNotNull(otherArgumentsTypes, "otherArgumentsTypes");

            var function = new Function(name)
            {
                ReturnType = firstArgumentType,
                Parameters = 
                {
                    new FunctionParameter("arg0", firstArgumentType),
                },
                Annotations =
                {
                    new LegacyServiceOperationAnnotation()
                    {
                        Method = HttpVerb.Get
                    },
                    new FunctionBodyAnnotation()
                    {
                        FunctionBody = CommonQueryBuilder.FunctionParameterReference("arg0"),
                    },
                }
            };

            for (int i = 0; i < otherArgumentsTypes.Length; i++)
            {
                function.Parameters.Add(new FunctionParameter("arg" + (i + 1).ToString(CultureInfo.InvariantCulture), otherArgumentsTypes[i]));
            }

            return function;
        }
        private void AddEntityTypeDrivenToggleActions(EntityModelSchema schema, EntitySet entitySet)
        {
            int functionCount = 0;

            var entityType = entitySet.EntityType;

            // Create a bunch of functions based on the entityType provided
            var toggleProperty = this.Random.ChooseFrom(entityType.AllProperties.Where(p => typeof(BooleanDataType).IsAssignableFrom(p.PropertyType.GetType())));

            var properties = entityType.Properties.Where(p => !p.IsStream()).AsEnumerable();

            var navigationProperties = entityType.NavigationProperties;

            var bindingTypes = new DataType[] { DataTypes.EntityType.WithDefinition(entityType), DataTypes.CollectionOfEntities(entityType) };

            // Create a function where this type is returned and a input parameter
            foreach (var memberProperty in properties)
            {
                if (memberProperty == toggleProperty)
                {
                    continue;
                }

                // Skip if we have used a datatype already
                if (this.useDataTypes.ContainsKey(memberProperty.PropertyType))
                {
                    continue;
                }
                else
                {
                    this.useDataTypes.Add(memberProperty.PropertyType, true);
                }

                foreach (var bindingDataType in bindingTypes)
                {
                    string funcNameString = bindingDataType is CollectionDataType ? "_FuncCollectionBound_" : "_Func_";
                    var function = new Function(entityType.NamespaceName, entityType.Name + "_" + memberProperty.Name + funcNameString + functionCount)
                    {
                        ReturnType = memberProperty.PropertyType,
                        Parameters = 
                        {
                            new FunctionParameter(entityType.Name, bindingDataType),
                            new FunctionParameter(memberProperty.Name, memberProperty.PropertyType)
                        },
                        Annotations =
                        {
                            new ServiceOperationAnnotation() { IsAction = true, BindingKind = OperationParameterBindingKind.Sometimes },
                            new ActionWithSingleParameterReturnedAnnotation(),
                        }
                    };

                    schema.Add(function);
                    functionCount++;

                    var noReturnFunction = new Function(entityType.NamespaceName, entityType.Name + "_" + memberProperty.Name + funcNameString + functionCount)
                    {
                        Parameters = 
                        {
                            new FunctionParameter(entityType.Name, bindingDataType),
                            new FunctionParameter(memberProperty.Name, memberProperty.PropertyType)
                        },
                        Annotations =
                        {
                            new ServiceOperationAnnotation() { IsAction = true },
                            new ToggleBoolPropertyValueActionAnnotation()                    
                            {
                                ToggleProperty = toggleProperty.Name,
                            }
                        }
                    };

                    schema.Add(noReturnFunction);
                    functionCount++;

                    var noParameterFunction = new Function(entityType.NamespaceName, entityType.Name + "_" + memberProperty.Name + funcNameString + functionCount)
                    {
                        ReturnType = memberProperty.PropertyType,
                        Parameters = 
                        {
                            new FunctionParameter(entityType.Name, bindingDataType),
                        },
                        Annotations =
                        {
                            new ServiceOperationAnnotation() { IsAction = true },
                            new ToggleBoolPropertyValueActionAnnotation()                    
                            {
                                ToggleProperty = toggleProperty.Name,
                                ReturnProperty = memberProperty.Name,
                            }
                        }
                    };

                    schema.Add(noParameterFunction);
                    functionCount++;
                }
            }

            // Create a function where this type is returned and a input parameter
            foreach (var navigationProperty in navigationProperties)
            {
                var navigationEntityType = navigationProperty.ToAssociationEnd.EntityType;
                DataType returnType = DataTypes.EntityType.WithDefinition(navigationEntityType);
                if (navigationProperty.ToAssociationEnd.Multiplicity == EndMultiplicity.Many)
                {
                    returnType = DataTypes.CollectionType.WithElementDataType(returnType);
                }

                // Skip if we have used a datatype already
                if (this.useDataTypes.ContainsKey(returnType))
                {
                    continue;
                }
                else
                {
                    this.useDataTypes.Add(returnType, true);
                }

                foreach (var bindingDataType in bindingTypes)
                {
                    string funcNameString = bindingDataType is CollectionDataType ? "_FuncCollectionBound_" : "_Func_";
                    var navigationFunction = new Function(entityType.NamespaceName, entityType.Name + "_" + navigationProperty.Name + funcNameString + functionCount)
                    {
                        ReturnType = returnType,
                        Parameters = 
                        {
                            new FunctionParameter(entityType.Name, bindingDataType),
                        },
                        Annotations =
                        {
                            new ServiceOperationAnnotation() { IsAction = true },
                            new ToggleBoolPropertyValueActionAnnotation()                    
                            {
                                ToggleProperty = toggleProperty.Name,
                                ReturnProperty = navigationProperty.Name,
                            }
                        }
                    };

                    schema.Add(navigationFunction);
                    functionCount++;

                    var noReturnNavigationFunction = new Function(entityType.NamespaceName, entityType.Name + "_" + navigationProperty.Name + funcNameString + functionCount)
                    {
                        Parameters = 
                        {
                            new FunctionParameter(entityType.Name, bindingDataType),
                        },
                        Annotations =
                        {
                            new ServiceOperationAnnotation() { IsAction = true },
                            new ToggleBoolPropertyValueActionAnnotation()                    
                            {
                                ToggleProperty = toggleProperty.Name,
                            }
                        }
                    };

                    schema.Add(noReturnNavigationFunction);
                    functionCount++;
                }
            }
        }