private void addRule(IEnumerable <string> tokens, RoutingTreeNode currentNode, Func <Parameters, ApiResponse> method)
        {
            if (tokens.Count() == 0)
            {
                if (currentNode.Method != null)
                {
                    // there is action already
                    throw new ArgumentException("Rule with this route was added before. Last token: " + currentNode.Token);
                }
                else
                {
                    // add method
                    currentNode.Method = method;
                }
                return;
            }
            else
            {
                string currentToken = tokens.First();
                // go deeper
                RoutingTreeNode nextNode =
                    currentNode.Children.Where(ch => ch.Token == currentToken).FirstOrDefault();
                if (nextNode == null)
                {
                    // create next node
                    nextNode = NodeFactory.Create(currentToken);
                    // add it to tree
                    currentNode.Children.Add(nextNode);
                }

                // go to next node
                addRule(tokens.Skip(1), nextNode, method);
            }
        }
        private ApiResponse route(IEnumerable <string> tokens, RoutingTreeNode currentNode, Parameters parameters)
        {
            if (tokens.Count() == 0)
            {
                // it's last token in route, execute method

                if (currentNode.IsAction)
                {
                    // if node contains api call
                    return(currentNode.Method(parameters));
                }
                else
                {
                    // if node doesn't contain api call
                    // check its children to be optional parameters
                    // for that, try to check it by passing empty string as token
                    RoutingTreeNode nextNode;
                    object          param;
                    if (tryCheckParameter(String.Empty, currentNode.Children, out nextNode, out param, requireMethod: true))
                    {
                        // add param to route parameters
                        parameters.Add(nextNode.Token, param);
                        // finally, execute method
                        return(nextNode.Method(parameters));
                    }
                    else
                    {
                        // error, bad url
                        return(new ApiResponse(System.Net.HttpStatusCode.BadRequest));
                    }
                }
            }
            else
            {
                // go deeper
                var currentToken = tokens.First();
                // firstly, check non-param tokens
                var nextNode = currentNode.Children
                               .Where(ch => !ch.IsParam &&
                                      ch.Token == currentToken)
                               .FirstOrDefault();
                if (nextNode == null)
                {
                    // if required token was not found,
                    // try to validate it as parameter
                    object param;
                    if (tryCheckParameter(currentToken, currentNode.Children, out nextNode, out param))
                    {
                        // add param to route parameters
                        parameters.Add(nextNode.Token, param);
                        // next node is initialized by out-keyword
                    }
                    else
                    {
                        // error, bad url
                        return(new ApiResponse("Error parsing url at: " + currentToken + ".", System.Net.HttpStatusCode.BadRequest));
                    }
                }
                // process remaining tokens from next node
                return(route(tokens.Skip(1), nextNode, parameters));
            }
        }
 public RoutingTree()
 {
     this.head = NodeFactory.Create(String.Empty);
 }
 private bool tryCheckParameter(string token, List <RoutingTreeNode> children, out RoutingTreeNode nextNode, out object parameter, bool requireMethod = false)
 {
     parameter = null;
     nextNode  = null;
     foreach (var ch in children)
     {
         // check all child parameters
         // if method is not reuiqred, (!requireMethod || ch.IsAction) == (!false || ..) == true
         if (ch.IsParam && (!requireMethod || ch.IsAction))
         {
             parameter = ch.CheckConstraint(token);
             // if parameter is valid (not null), return
             if (parameter != null)
             {
                 // set next node to node with valid parameter
                 nextNode = ch;
                 return(true);
             }
         }
     }
     // there are no parameters for this token
     return(false);
 }