Example #1
0
 public EndPointGroup(List<EndPointDescriptor> endPoints)
 {
     Trie = new Trie();
     EndPoints = endPoints;
     foreach (var endPoint in EndPoints) {
         var oneEndPointTrie = new Trie();
         oneEndPointTrie.Insert(endPoint.Path, endPoint);
         ValidatePathParameters(oneEndPointTrie, endPoint);
         Trie.Merge(oneEndPointTrie, endPoint.Path);
     }
 }
Example #2
0
        /// <summary>
        /// Validates that Path Parameters match the replaceable tokens in the 
        /// BaseRouteAttribute on the method.
        /// </summary>
        /// <param name="trie"></param>
        /// <param name="endPoint"></param>
        void ValidatePathParameters(Trie trie, EndPointDescriptor endPoint)
        {
            // If we don't have any parameters, we're done.
            if (endPoint.Parameters == null || !endPoint.Parameters.Any()) {
                return;
            }

            // If none of the parameters are Path Parameters, then we're done.
            var pathParameters = endPoint.Parameters.Where(p => p is PathAttribute);
            if (!pathParameters.Any()) {
                return;
            }

            var pathTokens = trie.GetReplaceableTokens(endPoint.Path);

            // Now, check that there are no replaceable tokens from the BaseRouteAttribute
            // that lack counterparts in the parameter list of the method.
            foreach (var pathParameter in pathParameters) {
                var matchingToken = pathTokens.SingleOrDefault(t =>
                    t.Equals(pathParameter.ParameterInfo.Name));

                if (matchingToken == null) {
                    throw new Exception(String.Format("FATAL ERROR - Method {0} has a path " +
                        "parameter {1} whose name is not found in the replaceable tokens in " +
                        "its BaseRoutePath {2}", endPoint.Name,
                        pathParameter.ParameterInfo.Name, endPoint.Path));
                }
            }

            // Just compare the counts now.
            var pathParametersCount = pathParameters.Count();
            var pathTokensCount = pathTokens.Count();

            if (pathParametersCount != pathTokensCount) {
                var parmNames = String.Join(",",
                        pathParameters.Select(pp => pp.ParameterInfo.Name));
                var tokenNames = String.Join(",", pathTokens);

                throw new Exception(String.Format("FATAL ERROR - Method {0} has {1} "
                  + "parameterized tokens in BaseRouteAttribute {2}, tokens = \"{3}\", and it "
                  + "has {4} method signature [Path] parameters \"{5}\".",
                    endPoint.Name, pathTokensCount, endPoint.Path, tokenNames,
                    pathParametersCount, parmNames));
            }
        }
Example #3
0
 /// <summary>
 /// Given an intermediate node, gets the list of terminal regexes for the various 
 /// sub-paths that sprout from it.
 /// </summary>
 /// <returns>A list of regexes, paired with the trie from *this* level that is the
 ///root to the matching sub-path.  Basically, each one just uses the passed-in trie
 /// as a value in the keyvaluepair, but this is called for each child, and aggregated;
 /// there's a different trie for each 'batch' of these.</returns>
 /// <param name="trie">Trie.</param>
 IEnumerable<KeyValuePair<Regex, Trie>> GetPathRegexes(Trie trie)
 {
     var regexes = new List<KeyValuePair<Regex, Trie>>();
     if (IsPathRegex) {
         regexes.Add(new KeyValuePair<Regex, Trie>(PathRegex, trie));
     }
     if (Children == null) {
         return regexes;
     }
     foreach (var child in Children) {
         regexes.AddRange(child.GetPathRegexes(trie)); // pass in parent Trie.
     }
     return regexes;
 }
Example #4
0
        /// <summary>
        /// Clone the trie node, but not its children.
        /// </summary>
        /// <param name="source">source</param>
        /// <param name="trieToMergePath">Path for node.</param>
        /// <param name="destination">Destination.</param>
        static void ShallowClone(Trie destination, Trie source, string trieToMergePath)
        {
            destination.Path = trieToMergePath;
            destination.Terminal = source.Terminal;
            destination.IsGreedyStar = source.IsGreedyStar;

            // Don't allow new shorter terminal paths to override the replaceable token nature
            // of longer paths that share a subpath.
            if (source.IsNodeReplaceableToken) {
                destination.IsNodeReplaceableToken = source.IsNodeReplaceableToken;
            }
            destination.PathRegex = source.PathRegex;
            destination.EndPoint = source.EndPoint;
        }
Example #5
0
        /// <summary>
        /// Merges a single path trie to an already-existing one.
        /// </summary>
        /// <param name="trieToMerge">Single path trie to merge.</param>
        /// <param name="trieToMergePath">Path for merging trie.</param>
        public void Merge(Trie trieToMerge, string trieToMergePath)
        {
            var thisTrie = this;
            var segs = trieToMergePath.Trim('/').Split('/');

            var i = 0;
            while (i < segs.Length) {
                trieToMerge = trieToMerge.Next(segs, i);
                if (trieToMerge == null) {
                    break;
                }
                var next = thisTrie.Next(segs, i, true);
                i++;

                // If Next returns null, or the segment it returns does not equal this
                // one, and it's not a replaceable token, then create a new one here.
                if (next == null ||
                    (!next.Segment.Equals(trieToMerge.Segment) &&
                    !next.IsNodeReplaceableToken)) {
                    if (thisTrie.Children == null) {
                        thisTrie.Children = new List<Trie>();
                    }
                    thisTrie.Children.Add(trieToMerge);
                    return;
                }
                thisTrie = next;
            }
            ShallowClone(thisTrie, trieToMerge, trieToMergePath);
        }
Example #6
0
        /// <summary>
        /// Loads up a trie with a complete path.
        /// </summary>
        /// <param name="path">Path to break into nodes and store.</param>
        /// <param name="endPoint">EndPointDescriptor to store at the terminus.</param>
        public void Insert(string path, IEndPointDescriptor endPoint)
        {
            var createRegex = false;
            var t = this;
            var inputSegments = path.Split('/');
            var regexPathSegments = new List<string>();

            var i = 0;
            foreach (var segment in inputSegments) {
                var greedyStar = false;
                var regexString = CheckIfReplaceableToken(segment, out greedyStar);
                regexPathSegments.Add(regexString ?? segment);
                var segmentIsRegex = !string.IsNullOrEmpty(regexString);
                createRegex = createRegex | segmentIsRegex;

                var next = t.Next(inputSegments, i++);
                if (next == null) {
                    next = new Trie(segment, segmentIsRegex, greedyStar);
                    if (t.Children == null) {
                        t.Children = new List<Trie>();
                    }
                    t.Children.Add(next);
                }
                t = next;
                if (greedyStar) {
                    break;
                }
            }
            t.Terminal = true;
            t.Path = path;
            t.EndPoint = endPoint;
            if (!createRegex) {
                return;
            }
            t.PathRegex = new Regex("^/?" + string.Join("/", regexPathSegments) + "$",
                RegexOptions.Compiled);
        }