/// <summary>
        /// Generates all templates for an input function.
        /// </summary>
        /// <param name="segment">The input function.</param>
        /// <returns>All templates.</returns>
        public static IList <FunctionSegmentTemplate> GenerateFunctionSegments(this FunctionSegmentTemplate segment)
        {
            if (segment == null)
            {
                throw new ArgumentNullException(nameof(segment));
            }

            // split parameters
            (var fixes, var optionals) = SplitParameters(segment.Function);

            // gets all combinations of the optional parameters.
            Stack <IEdmOptionalParameter>   current = new Stack <IEdmOptionalParameter>();
            IList <IEdmOptionalParameter[]> full    = new List <IEdmOptionalParameter[]>();
            int length = optionals.Count;

            if (optionals.Count > 0)
            {
                Traveral(optionals.ToArray(), 0, length, current, full);
            }

            IList <FunctionSegmentTemplate> newList = new List <FunctionSegmentTemplate>();

            foreach (var optional in full)
            {
                ISet <string> requiredParameters = new HashSet <string>(fixes.Select(e => e.Name));
                foreach (var optionalParameter in optional)
                {
                    requiredParameters.Add(optionalParameter.Name);
                }

                newList.Add(new FunctionSegmentTemplate(segment.Function, segment.NavigationSource, requiredParameters));
            }

            return(newList);
        }
        private static string GetDisplayName(this FunctionSegmentTemplate segment, bool qualified)
        {
            Contract.Assert(segment != null);

            string parameters = "(" + string.Join(",", segment.ParameterMappings.Select(a => $"{a.Key}={{{a.Value}}}")) + ")";

            if (qualified)
            {
                return(segment.Function.FullName() + parameters);
            }
            else
            {
                return(segment.Function.Name + parameters);
            }
        }
        /// <summary>
        /// Generates all templates for the given <see cref="ODataPathTemplate"/>.
        /// All templates mean:
        /// 1) for key segment, we have key in parenthesis and key as segment.
        /// 2) for bound function segment, we have qualified function call and unqualified function call.
        /// </summary>
        /// <param name="path">The given path template.</param>
        /// <param name="options">The route options.</param>
        /// <returns>All path templates.</returns>
        public static IEnumerable <string> GetTemplates(this ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            options = options ?? ODataRouteOptions.Default;

            IList <StringBuilder> templates = new List <StringBuilder>
            {
                new StringBuilder()
            };

            int count = path.Segments.Count;

            for (int index = 0; index < count; index++)
            {
                ODataSegmentTemplate segment = path.Segments[index];

                if (segment.Kind == ODataSegmentKind.Key)
                {
                    // for key segment, if it's single key, let's add key as segment template also
                    // otherwise, we only add the key in parenthesis template.
                    KeySegmentTemplate keySg = segment as KeySegmentTemplate;
                    templates = AppendKeyTemplate(templates, keySg, options);
                    continue;
                }

                if (index != 0)
                {
                    templates = CombinateTemplate(templates, "/");
                }

                // create =>  ~.../navigation/{key}/$ref
                if (segment.Kind == ODataSegmentKind.NavigationLink)
                {
                    NavigationLinkSegmentTemplate navigationLinkSegment = (NavigationLinkSegmentTemplate)segment;
                    if (index == count - 1)
                    {
                        // we don't have the other segment
                        templates = CombinateTemplates(templates, $"{navigationLinkSegment.Segment.NavigationProperty.Name}/$ref");
                    }
                    else
                    {
                        ODataSegmentTemplate nextSegment = path.Segments[index + 1];
                        if (nextSegment.Kind == ODataSegmentKind.Key)
                        {
                            // append "navigation property"
                            templates = CombinateTemplates(templates, navigationLinkSegment.Segment.NavigationProperty.Name);

                            // append "key"
                            KeySegmentTemplate keySg = nextSegment as KeySegmentTemplate;
                            templates = AppendKeyTemplate(templates, keySg, options);

                            // append $ref
                            templates = CombinateTemplates(templates, "/$ref");
                            index++; // skip the key segment after $ref.
                        }
                        else
                        {
                            templates = CombinateTemplates(templates, $"{navigationLinkSegment.Segment.NavigationProperty.Name}/$ref");
                        }
                    }

                    continue;
                }

                if (segment.Kind == ODataSegmentKind.Action)
                {
                    ActionSegmentTemplate action = (ActionSegmentTemplate)segment;
                    templates = AppendActionTemplate(templates, action, options);
                }
                else if (segment.Kind == ODataSegmentKind.Function)
                {
                    FunctionSegmentTemplate function = (FunctionSegmentTemplate)segment;
                    templates = AppendFunctionTemplate(templates, function, options);
                }
                else
                {
                    templates = CombinateTemplate(templates, segment.Literal);
                }
            }

            return(templates.Select(t => t.ToString()));
        }
        private static IList <StringBuilder> AppendFunctionTemplate(IList <StringBuilder> templates, FunctionSegmentTemplate segment, ODataRouteOptions options)
        {
            Contract.Assert(segment != null);
            Contract.Assert(options != null);

            if (options.EnableQualifiedOperationCall && options.EnableUnqualifiedOperationCall)
            {
                return(CombinateTemplates(templates, segment.Literal, segment.UnqualifiedIdentifier));
            }
            else if (options.EnableQualifiedOperationCall)
            {
                return(CombinateTemplate(templates, segment.Literal));
            }
            else if (options.EnableUnqualifiedOperationCall)
            {
                return(CombinateTemplate(templates, segment.UnqualifiedIdentifier));
            }
            else
            {
                throw new ODataException(Error.Format(SRResources.RouteOptionDisabledOperationSegment, "function"));
            }
        }
        private static IList <TemplateInfo> AppendFunctionTemplate(IList <TemplateInfo> templates, FunctionSegmentTemplate segment, ODataRouteOptions options)
        {
            Contract.Assert(segment != null);
            Contract.Assert(options != null);

            string qualified  = segment.GetDisplayName(true);
            string unqulified = segment.GetDisplayName(false);

            if (options.EnableQualifiedOperationCall && options.EnableUnqualifiedOperationCall)
            {
                return(CombinateTemplates(templates, (segment.Literal, qualified), (segment.UnqualifiedIdentifier, unqulified)));
            }
            else if (options.EnableQualifiedOperationCall)
            {
                return(CombinateTemplate(templates, (segment.Literal, qualified)));
            }
            else if (options.EnableUnqualifiedOperationCall)
            {
                return(CombinateTemplate(templates, (segment.UnqualifiedIdentifier, unqulified)));
            }
            else
            {
                throw new ODataException(Error.Format(SRResources.RouteOptionDisabledOperationSegment, "function"));
            }
        }
        /// <summary>
        /// Generates all templates for the given <see cref="ODataPathTemplate"/>
        /// </summary>
        /// <param name="path">The given path template.</param>
        /// <returns>All path templates.</returns>
        public static IEnumerable <string> GetTemplates(this ODataPathTemplate path)
        {
            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            IList <StringBuilder> templates = new List <StringBuilder>
            {
                new StringBuilder()
            };

            int count = path.Segments.Count;

            for (int index = 0; index < count; index++)
            {
                ODataSegmentTemplate segment = path.Segments[index];

                if (segment.Kind == ODataSegmentKind.Key)
                {
                    KeySegmentTemplate keySg = segment as KeySegmentTemplate;
                    if (keySg.Count == 1)
                    {
                        templates = CombinateTemplates(templates, "(" + segment.Literal + ")", "/" + segment.Literal);
                    }
                    else
                    {
                        templates = CombinateTemplate(templates, "(" + segment.Literal + ")");
                    }

                    continue;
                }

                if (index != 0)
                {
                    templates = CombinateTemplate(templates, "/");
                }

                // create =>  ~.../navigation/{key}/$ref
                if (segment.Kind == ODataSegmentKind.NavigationLink)
                {
                    NavigationPropertyLinkSegmentTemplate navigationLinkSegment = (NavigationPropertyLinkSegmentTemplate)segment;
                    if (index == count - 1)
                    {
                        // we don't have the other segment
                        templates = CombinateTemplates(templates, $"{navigationLinkSegment.Segment.NavigationProperty.Name}/$ref");
                    }
                    else
                    {
                        ODataSegmentTemplate nextSegment = path.Segments[index + 1];
                        if (nextSegment.Kind == ODataSegmentKind.Key)
                        {
                            // append "navigation property"
                            templates = CombinateTemplates(templates, navigationLinkSegment.Segment.NavigationProperty.Name);

                            // append "key"
                            KeySegmentTemplate keySg = nextSegment as KeySegmentTemplate;
                            if (keySg.Count == 1)
                            {
                                templates = CombinateTemplates(templates, "(" + nextSegment.Literal + ")", "/" + nextSegment.Literal);
                            }
                            else
                            {
                                templates = CombinateTemplate(templates, "(" + nextSegment.Literal + ")");
                            }

                            // append $ref
                            templates = CombinateTemplates(templates, "/$ref");
                            index++; // skip the key segment after $ref.
                        }
                        else
                        {
                            templates = CombinateTemplates(templates, $"{navigationLinkSegment.Segment.NavigationProperty.Name}/$ref");
                        }
                    }

                    continue;
                }

                if (segment.Kind == ODataSegmentKind.Action)
                {
                    ActionSegmentTemplate action = (ActionSegmentTemplate)segment;
                    templates = CombinateTemplates(templates, action.Action.FullName(), action.Action.Name);
                }
                else if (segment.Kind == ODataSegmentKind.Function)
                {
                    FunctionSegmentTemplate function = (FunctionSegmentTemplate)segment;
                    templates = CombinateTemplates(templates, function.Literal, function.UnqualifiedIdentifier);
                }
                else
                {
                    templates = CombinateTemplate(templates, segment.Literal);
                }
            }

            return(templates.Select(t => t.ToString()));
        }
        /// <summary>
        /// Gets the whole supported template belongs to a <see cref="ODataPathTemplate"/>.
        /// We supports:
        /// 1. Key as segment if it's single key (We doesn't consider the alternate key so far)
        /// 2. Unqualified function/action call
        /// 3. Optional parameters combination.
        /// </summary>
        /// <param name="path">The input path template.</param>
        /// <returns>The whole path template string.</returns>
        public static IEnumerable <string> GetAllTemplates(this ODataPathTemplate path)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            IList <StringBuilder> templates = new List <StringBuilder>
            {
                new StringBuilder()
            };

            int index = 0;

            foreach (ODataSegmentTemplate segment in path.Segments)
            {
                if (segment.Kind == ODataSegmentKind.Key)
                {
                    KeySegmentTemplate keySg = segment as KeySegmentTemplate;
                    if (keySg.Count == 1)
                    {
                        templates = CombinateTemplates(templates, "(" + segment.Literal + ")", "/" + segment.Literal);
                    }
                    else
                    {
                        templates = CombinateTemplate(templates, "(" + segment.Literal + ")");
                    }

                    index++;
                    continue;
                }

                if (index != 0)
                {
                    templates = CombinateTemplate(templates, "/");
                }
                index++;

                if (segment.Kind == ODataSegmentKind.Action)
                {
                    ActionSegmentTemplate action = (ActionSegmentTemplate)segment;
                    templates = CombinateTemplates(templates, action.Action.FullName(), action.Action.Name);
                }
                else if (segment.Kind == ODataSegmentKind.Function)
                {
                    FunctionSegmentTemplate function          = (FunctionSegmentTemplate)segment;
                    IList <string>          functionTemplates = function.Function.GenerateFunctionTemplates();
                    templates = CombinateTemplates(templates, functionTemplates);
                }
                else if (segment.Kind == ODataSegmentKind.FunctionImport)
                {
                    FunctionImportSegmentTemplate functionImport = (FunctionImportSegmentTemplate)segment;
                    IList <string> functionTemplates             = functionImport.FunctionImport.Function.GenerateFunctionTemplates();
                    templates = CombinateTemplates(templates, functionTemplates);
                }
                else
                {
                    templates = CombinateTemplate(templates, segment.Literal);
                }
            }

            return(templates.Select(t => t.ToString()));
        }