示例#1
0
            public Enumerator(PathTokenizer tokenizer)
            {
                _path = tokenizer._path;

                _index  = -1;
                _length = -1;
            }
        public void PathTokenizer_Enumerator(string path, StringSegment[] expectedSegments)
        {
            // Arrange
            var tokenizer = new PathTokenizer(new PathString(path));

            // Act & Assert
            Assert.Equal <StringSegment>(expectedSegments, tokenizer);
        }
        public void PathTokenizer_Indexer(string path, StringSegment[] expectedSegments)
        {
            // Arrange
            var tokenizer = new PathTokenizer(new PathString(path));

            // Act & Assert
            for (var i = 0; i < expectedSegments.Length; i++)
            {
                Assert.Equal(expectedSegments[i], tokenizer[i]);
            }
        }
        public void PathTokenizer_Count(string path, StringSegment[] expectedSegments)
        {
            // Arrange
            var tokenizer = new PathTokenizer(new PathString(path));

            // Act
            var count = tokenizer.Count;

            // Assert
            Assert.Equal(expectedSegments.Length, count);
        }
示例#5
0
        public bool TryMatch(PathString path, RouteValueDictionary values)
        {
            if (values == null)
            {
                throw new ArgumentNullException(nameof(values));
            }

            var i             = 0;
            var pathTokenizer = new PathTokenizer(path);

            // Perf: We do a traversal of the request-segments + route-segments twice.
            //
            // For most segment-types, we only really need to any work on one of the two passes.
            //
            // On the first pass, we're just looking to see if there's anything that would disqualify us from matching.
            // The most common case would be a literal segment that doesn't match.
            //
            // On the second pass, we're almost certainly going to match the URL, so go ahead and allocate the 'values'
            // and start capturing strings.
            foreach (var stringSegment in pathTokenizer)
            {
                if (stringSegment.Length == 0)
                {
                    return(false);
                }

                var pathSegment = i >= RoutePattern.PathSegments.Count ? null : RoutePattern.PathSegments[i];
                if (pathSegment == null && stringSegment.Length > 0)
                {
                    // If pathSegment is null, then we're out of route segments. All we can match is the empty
                    // string.
                    return(false);
                }
                else if (pathSegment.IsSimple && pathSegment.Parts[0] is RoutePatternParameterPart parameter && parameter.IsCatchAll)
                {
                    // Nothing to validate for a catch-all - it can match any string, including the empty string.
                    //
                    // Also, a catch-all has to be the last part, so we're done.
                    break;
                }
                if (!TryMatchLiterals(i++, stringSegment, pathSegment))
                {
                    return(false);
                }
            }

            for (; i < RoutePattern.PathSegments.Count; i++)
            {
                // We've matched the request path so far, but still have remaining route segments. These need
                // to be all single-part parameter segments with default values or else they won't match.
                var pathSegment = RoutePattern.PathSegments[i];
                Debug.Assert(pathSegment != null);

                if (!pathSegment.IsSimple)
                {
                    // If the segment is a complex segment, it MUST contain literals, and we've parsed the full
                    // path so far, so it can't match.
                    return(false);
                }

                var part = pathSegment.Parts[0];
                if (part.IsLiteral || part.IsSeparator)
                {
                    // If the segment is a simple literal - which need the URL to provide a value, so we don't match.
                    return(false);
                }

                var parameter = (RoutePatternParameterPart)part;
                if (parameter.IsCatchAll)
                {
                    // Nothing to validate for a catch-all - it can match any string, including the empty string.
                    //
                    // Also, a catch-all has to be the last part, so we're done.
                    break;
                }

                // If we get here, this is a simple segment with a parameter. We need it to be optional, or for the
                // defaults to have a value.
                if (!_hasDefaultValue[i] && !parameter.IsOptional)
                {
                    // There's no default for this (non-optional) parameter so it can't match.
                    return(false);
                }
            }

            // At this point we've very likely got a match, so start capturing values for real.
            i = 0;
            foreach (var requestSegment in pathTokenizer)
            {
                var pathSegment = RoutePattern.PathSegments[i++];
                if (SavePathSegmentsAsValues(i, values, requestSegment, pathSegment))
                {
                    break;
                }
                if (!pathSegment.IsSimple)
                {
                    if (!MatchComplexSegment(pathSegment, requestSegment.AsSpan(), values))
                    {
                        return(false);
                    }
                }
            }

            for (; i < RoutePattern.PathSegments.Count; i++)
            {
                // We've matched the request path so far, but still have remaining route segments. We already know these
                // are simple parameters that either have a default, or don't need to produce a value.
                var pathSegment = RoutePattern.PathSegments[i];
                Debug.Assert(pathSegment != null);
                Debug.Assert(pathSegment.IsSimple);

                var part = pathSegment.Parts[0];
                Debug.Assert(part.IsParameter);

                // It's ok for a catch-all to produce a null value
                if (part is RoutePatternParameterPart parameter && (parameter.IsCatchAll || _hasDefaultValue[i]))
                {
                    // Don't replace an existing value with a null.
                    var defaultValue = _defaultValues[i];
                    if (defaultValue != null || !values.ContainsKey(parameter.Name))
                    {
                        values[parameter.Name] = defaultValue;
                    }
                }
            }

            // Copy all remaining default values to the route data
            foreach (var kvp in Defaults)
            {
#if RVD_TryAdd
                values.TryAdd(kvp.Key, kvp.Value);
#else
                if (!values.ContainsKey(kvp.Key))
                {
                    values.Add(kvp.Key, kvp.Value);
                }
#endif
            }

            return(true);
        }