public void AcceptDefault(string key) { Debug.Assert(!_acceptedValues.ContainsKey(key)); object value; if (_defaults != null && _defaults.TryGetValue(key, out value)) { _acceptedValues.Add(key, value); } }
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); }
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); }
// 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); }
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()); }