Example #1
0
            public void AcceptDefault(string key)
            {
                Debug.Assert(!_acceptedValues.ContainsKey(key));

                object value;

                if (_defaults != null && _defaults.TryGetValue(key, out value))
                {
                    _acceptedValues.Add(key, value);
                }
            }
Example #2
0
        private bool IsMatch(IRoutePatternAddress address, DispatcherValueCollection values)
        {
            foreach (var kvp in address.Defaults)
            {
                values.TryGetValue(kvp.Key, out var value);

                if (!string.Equals(Convert.ToString(kvp.Value) ?? string.Empty, Convert.ToString(value) ?? string.Empty, StringComparison.OrdinalIgnoreCase))
                {
                    return(false);
                }
            }

            return(true);
        }
        public void TryGetValue_PropertyStorage_True_CaseInsensitive()
        {
            // Arrange
            var dict = new DispatcherValueCollection(new { key = "value" });

            // Act
            object value;
            var    result = dict.TryGetValue("kEy", out value);

            // Assert
            Assert.True(result);
            Assert.Equal("value", value);
            Assert.IsType <DispatcherValueCollection.PropertyStorage>(dict._storage);
        }
        public void TryGetValue_PropertyStorage_False()
        {
            // Arrange
            var dict = new DispatcherValueCollection(new { key = "value" });

            // Act
            object value;
            var    result = dict.TryGetValue("other", out value);

            // Assert
            Assert.False(result);
            Assert.Null(value);
            Assert.IsType <DispatcherValueCollection.PropertyStorage>(dict._storage);
        }
        public void TryGetValue_EmptyStorage()
        {
            // Arrange
            var dict = new DispatcherValueCollection();

            // Act
            object value;
            var    result = dict.TryGetValue("key", out value);

            // Assert
            Assert.False(result);
            Assert.Null(value);
            Assert.IsType <DispatcherValueCollection.EmptyStorage>(dict._storage);
        }
        public void TryGetValue_ListStorage_True()
        {
            // Arrange
            var dict = new DispatcherValueCollection()
            {
                { "key", "value" },
            };

            // Act
            object value;
            var    result = dict.TryGetValue("key", out value);

            // Assert
            Assert.True(result);
            Assert.Equal("value", value);
            Assert.IsType <DispatcherValueCollection.ListStorage>(dict._storage);
        }
Example #7
0
        private bool MatchConstraints(HttpContext httpContext, DispatcherValueCollection values, IDictionary <string, IDispatcherValueConstraint> constraints)
        {
            if (constraints != null)
            {
                foreach (var kvp in constraints)
                {
                    var constraint        = kvp.Value;
                    var constraintContext = new DispatcherValueConstraintContext(httpContext, values, ConstraintPurpose.IncomingRequest)
                    {
                        Key = kvp.Key
                    };

                    if (!constraint.Match(constraintContext))
                    {
                        values.TryGetValue(kvp.Key, out var value);

                        Logger.RouteValueDoesNotMatchConstraint(value, kvp.Key, kvp.Value);
                        return(false);
                    }
                }
            }

            return(true);
        }
Example #8
0
        // Step 1: Get the list of values we're going to try to use to match and generate this URI
        public (DispatcherValueCollection acceptedValues, DispatcherValueCollection combinedValues) GetValues(DispatcherValueCollection ambientValues, DispatcherValueCollection values)
        {
            var context = new TemplateBindingContext(_defaults);

            // Find out which entries in the URI are valid for the URI we want to generate.
            // If the URI had ordered parameters a="1", b="2", c="3" and the new values
            // specified that b="9", then we need to invalidate everything after it. The new
            // values should then be a="1", b="9", c=<no value>.
            //
            // We also handle the case where a parameter is optional but has no value - we shouldn't
            // accept additional parameters that appear *after* that parameter.
            for (var i = 0; i < _pattern.Parameters.Count; i++)
            {
                var parameter = _pattern.Parameters[i];

                // If it's a parameter subsegment, examine the current value to see if it matches the new value
                var parameterName = parameter.Name;

                var hasNewParameterValue = values.TryGetValue(parameterName, out var newParameterValue);

                object currentParameterValue    = null;
                var    hasCurrentParameterValue = ambientValues != null &&
                                                  ambientValues.TryGetValue(parameterName, out currentParameterValue);

                if (hasNewParameterValue && hasCurrentParameterValue)
                {
                    if (!RoutePartsEqual(currentParameterValue, newParameterValue))
                    {
                        // Stop copying current values when we find one that doesn't match
                        break;
                    }
                }

                if (!hasNewParameterValue &&
                    !hasCurrentParameterValue &&
                    _defaults?.ContainsKey(parameter.Name) != true)
                {
                    // This is an unsatisfied parameter value and there are no defaults. We might still
                    // be able to generate a URL but we should stop 'accepting' ambient values.
                    //
                    // This might be a case like:
                    //  template: a/{b?}/{c?}
                    //  ambient: { c = 17 }
                    //  values: { }
                    //
                    // We can still generate a URL from this ("/a") but we shouldn't accept 'c' because
                    // we can't use it.
                    //
                    // In the example above we should fall into this block for 'b'.
                    break;
                }

                // If the parameter is a match, add it to the list of values we will use for URI generation
                if (hasNewParameterValue)
                {
                    if (IsRoutePartNonEmpty(newParameterValue))
                    {
                        context.Accept(parameterName, newParameterValue);
                    }
                }
                else
                {
                    if (hasCurrentParameterValue)
                    {
                        context.Accept(parameterName, currentParameterValue);
                    }
                }
            }

            // Add all remaining new values to the list of values we will use for URI generation
            foreach (var kvp in values)
            {
                if (IsRoutePartNonEmpty(kvp.Value))
                {
                    context.Accept(kvp.Key, kvp.Value);
                }
            }

            // Accept all remaining default values if they match a required parameter
            for (var i = 0; i < _pattern.Parameters.Count; i++)
            {
                var parameter = _pattern.Parameters[i];
                if (parameter.IsOptional || parameter.IsCatchAll)
                {
                    continue;
                }

                if (context.NeedsValue(parameter.Name))
                {
                    // Add the default value only if there isn't already a new value for it and
                    // only if it actually has a default value, which we determine based on whether
                    // the parameter value is required.
                    context.AcceptDefault(parameter.Name);
                }
            }

            // Validate that all required parameters have a value.
            for (var i = 0; i < _pattern.Parameters.Count; i++)
            {
                var parameter = _pattern.Parameters[i];
                if (parameter.IsOptional || parameter.IsCatchAll)
                {
                    continue;
                }

                if (!context.AcceptedValues.ContainsKey(parameter.Name))
                {
                    // We don't have a value for this parameter, so we can't generate a url.
                    return(null, null);
                }
            }

            // Any default values that don't appear as parameters are treated like filters. Any new values
            // provided must match these defaults.
            foreach (var filter in _filters)
            {
                var parameter = _pattern.GetParameter(filter.Key);
                if (parameter != null)
                {
                    continue;
                }

                if (values.TryGetValue(filter.Key, out var value))
                {
                    if (!RoutePartsEqual(value, filter.Value))
                    {
                        // If there is a non-parameterized value in the route and there is a
                        // new value for it and it doesn't match, this route won't match.
                        return(null, null);
                    }
                }
            }

            // Add any ambient values that don't match parameters - they need to be visible to constraints
            // but they will ignored by link generation.
            var combinedValues = new DispatcherValueCollection(context.AcceptedValues);

            if (ambientValues != null)
            {
                foreach (var kvp in ambientValues)
                {
                    if (IsRoutePartNonEmpty(kvp.Value))
                    {
                        var parameter = _pattern.GetParameter(kvp.Key);
                        if (parameter == null && !context.AcceptedValues.ContainsKey(kvp.Key))
                        {
                            combinedValues.Add(kvp.Key, kvp.Value);
                        }
                    }
                }
            }

            return(context.AcceptedValues, combinedValues);
        }
Example #9
0
        private string BindValues(UriBuildingContext context, DispatcherValueCollection acceptedValues)
        {
            for (var i = 0; i < _pattern.PathSegments.Count; i++)
            {
                Debug.Assert(context.BufferState == SegmentState.Beginning);
                Debug.Assert(context.UriState == SegmentState.Beginning);

                var segment = _pattern.PathSegments[i];

                for (var j = 0; j < segment.Parts.Count; j++)
                {
                    var part = segment.Parts[j];

                    if (part.IsLiteral)
                    {
                        if (!context.Accept(_urlEncoder, ((RoutePatternLiteral)part).Content))
                        {
                            return(null);
                        }
                    }
                    else if (part.IsSeparator)
                    {
                        if (!context.Accept(_urlEncoder, ((RoutePatternSeparator)part).Content))
                        {
                            return(null);
                        }
                    }
                    else if (part.IsParameter && part is RoutePatternParameter parameter)
                    {
                        // If it's a parameter, get its value
                        object value;
                        var    hasValue = acceptedValues.TryGetValue(parameter.Name, out value);
                        if (hasValue)
                        {
                            acceptedValues.Remove(parameter.Name);
                        }

                        var    isSameAsDefault = false;
                        object defaultValue;
                        if (_defaults != null && _defaults.TryGetValue(parameter.Name, out defaultValue))
                        {
                            if (RoutePartsEqual(value, defaultValue))
                            {
                                isSameAsDefault = true;
                            }
                        }

                        var converted = Convert.ToString(value, CultureInfo.InvariantCulture);
                        if (isSameAsDefault)
                        {
                            // If the accepted value is the same as the default value buffer it since
                            // we won't necessarily add it to the URI we generate.
                            if (!context.Buffer(_urlEncoder, converted))
                            {
                                return(null);
                            }
                        }
                        else
                        {
                            // If the value is not accepted, it is null or empty value in the
                            // middle of the segment. We accept this if the parameter is an
                            // optional parameter and it is preceded by an optional seperator.
                            // I this case, we need to remove the optional seperator that we
                            // have added to the URI
                            // Example: template = {id}.{format?}. parameters: id=5
                            // In this case after we have generated "5.", we wont find any value
                            // for format, so we remove '.' and generate 5.
                            if (!context.Accept(_urlEncoder, converted))
                            {
                                if (j != 0 && parameter.IsOptional && segment.Parts[j - 1].IsSeparator)
                                {
                                    context.Remove(((RoutePatternSeparator)segment.Parts[j - 1]).Content);
                                }
                                else
                                {
                                    return(null);
                                }
                            }
                        }
                    }
                }

                context.EndSegment();
            }

            // Generate the query string from the remaining values
            var wroteFirst = false;

            foreach (var kvp in acceptedValues)
            {
                if (_defaults != null && _defaults.ContainsKey(kvp.Key))
                {
                    // This value is a 'filter' we don't need to put it in the query string.
                    continue;
                }

                var values = kvp.Value as IEnumerable;
                if (values != null && !(values is string))
                {
                    foreach (var value in values)
                    {
                        wroteFirst |= AddParameterToContext(context, kvp.Key, value, wroteFirst);
                    }
                }
                else
                {
                    wroteFirst |= AddParameterToContext(context, kvp.Key, kvp.Value, wroteFirst);
                }
            }
            return(context.ToString());
        }