// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Microsoft.AspNet.Routing.Constraints; namespace Microsoft.AspNet.Routing { /// /// A builder for produding a mapping of keys to see . /// /// /// allows iterative building a set of route constraints, and will /// merge multiple entries for the same key. /// public class RouteConstraintBuilder { private readonly IInlineConstraintResolver _inlineConstraintResolver; private readonly string _displayName; private readonly Dictionary> _constraints; private readonly HashSet _optionalParameters; /// /// Creates a new instance. /// /// The . /// The display name (for use in error messages). public RouteConstraintBuilder( IInlineConstraintResolver inlineConstraintResolver, string displayName) { if (inlineConstraintResolver == null) { throw new ArgumentNullException(nameof(inlineConstraintResolver)); } if (displayName == null) { throw new ArgumentNullException(nameof(displayName)); } _inlineConstraintResolver = inlineConstraintResolver; _displayName = displayName; _constraints = new Dictionary>(StringComparer.OrdinalIgnoreCase); _optionalParameters = new HashSet(StringComparer.OrdinalIgnoreCase); } /// /// Builds a mapping of constraints. /// /// An of the constraints. public IDictionary Build() { var constraints = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var kvp in _constraints) { IRouteConstraint constraint; if (kvp.Value.Count == 1) { constraint = kvp.Value[0]; } else { constraint = new CompositeRouteConstraint(kvp.Value.ToArray()); } if (_optionalParameters.Contains(kvp.Key)) { var optionalConstraint = new OptionalRouteConstraint(constraint); constraints.Add(kvp.Key, optionalConstraint); } else { constraints.Add(kvp.Key, constraint); } } return constraints; } /// /// Adds a constraint instance for the given key. /// /// The key. /// /// The constraint instance. Must either be a string or an instance of . /// /// /// If the is a string, it will be converted to a . /// /// For example, the string Product[0-9]+ will be converted to the regular expression /// ^(Product[0-9]+). See for more details. /// public void AddConstraint(string key, object value) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (value == null) { throw new ArgumentNullException(nameof(value)); } var constraint = value as IRouteConstraint; if (constraint == null) { var regexPattern = value as string; if (regexPattern == null) { throw new InvalidOperationException( Resources.FormatRouteConstraintBuilder_ValidationMustBeStringOrCustomConstraint( key, value, _displayName, typeof(IRouteConstraint))); } var constraintsRegEx = "^(" + regexPattern + ")$"; constraint = new RegexRouteConstraint(constraintsRegEx); } Add(key, constraint); } /// /// Adds a constraint for the given key, resolved by the . /// /// The key. /// The text to be resolved by . /// /// The can create instances /// based on . See to register /// custom constraint types. /// public void AddResolvedConstraint(string key, string constraintText) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (constraintText == null) { throw new ArgumentNullException(nameof(constraintText)); } var constraint = _inlineConstraintResolver.ResolveConstraint(constraintText); if (constraint == null) { throw new InvalidOperationException( Resources.FormatRouteConstraintBuilder_CouldNotResolveConstraint( key, constraintText, _displayName, _inlineConstraintResolver.GetType().Name)); } Add(key, constraint); } /// /// Sets the given key as optional. /// /// The key. public void SetOptional(string key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } _optionalParameters.Add(key); } private void Add(string key, IRouteConstraint constraint) { List list; if (!_constraints.TryGetValue(key, out list)) { list = new List(); _constraints.Add(key, list); } list.Add(constraint); } } }