// 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);
}
}
}