147 lines
6.3 KiB
C#
147 lines
6.3 KiB
C#
// Copyright (c) Microsoft Open Technologies, Inc. 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 System.Globalization;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using Microsoft.AspNet.Routing.Constraints;
|
|
|
|
namespace Microsoft.AspNet.Routing
|
|
{
|
|
/// <summary>
|
|
/// The default implementation of <see cref="IInlineConstraintResolver"/>. Resolves constraints by parsing
|
|
/// a constraint key and constraint arguments, using a map to resolve the constraint type, and calling an
|
|
/// appropriate constructor for the constraint type.
|
|
/// </summary>
|
|
public class DefaultInlineConstraintResolver : IInlineConstraintResolver
|
|
{
|
|
private readonly IDictionary<string, Type> _inlineConstraintMap = GetDefaultConstraintMap();
|
|
|
|
/// <summary>
|
|
/// Gets the mutable dictionary that maps constraint keys to a particular constraint type.
|
|
/// </summary>
|
|
public IDictionary<string, Type> ConstraintMap
|
|
{
|
|
get
|
|
{
|
|
return _inlineConstraintMap;
|
|
}
|
|
}
|
|
|
|
private static IDictionary<string, Type> GetDefaultConstraintMap()
|
|
{
|
|
return new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
// Type-specific constraints
|
|
{ "int", typeof(IntRouteConstraint) },
|
|
};
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
/// <example>
|
|
/// A typical constraint looks like the following
|
|
/// "exampleConstraint(arg1, arg2, 12)".
|
|
/// Here if the type registered for exampleConstraint has a single constructor with one argument,
|
|
/// The entire string "arg1, arg2, 12" will be treated as a single argument.
|
|
/// In all other cases arguments are split at comma.
|
|
/// </example>
|
|
public virtual IRouteConstraint ResolveConstraint([NotNull] string inlineConstraint)
|
|
{
|
|
string constraintKey;
|
|
string argumentString;
|
|
int indexOfFirstOpenParens = inlineConstraint.IndexOf('(');
|
|
if (indexOfFirstOpenParens >= 0 && inlineConstraint.EndsWith(")", StringComparison.Ordinal))
|
|
{
|
|
constraintKey = inlineConstraint.Substring(0, indexOfFirstOpenParens);
|
|
argumentString = inlineConstraint.Substring(indexOfFirstOpenParens + 1,
|
|
inlineConstraint.Length - indexOfFirstOpenParens - 2);
|
|
}
|
|
else
|
|
{
|
|
constraintKey = inlineConstraint;
|
|
argumentString = null;
|
|
}
|
|
|
|
Type constraintType;
|
|
if (!_inlineConstraintMap.TryGetValue(constraintKey, out constraintType))
|
|
{
|
|
// Cannot resolve the constraint key
|
|
return null;
|
|
}
|
|
|
|
if (!typeof(IRouteConstraint).GetTypeInfo().IsAssignableFrom(constraintType.GetTypeInfo()))
|
|
{
|
|
throw new InvalidOperationException(
|
|
Resources.FormatDefaultInlineConstraintResolver_TypeNotConstraint(
|
|
constraintType, constraintKey, typeof(IRouteConstraint).Name));
|
|
}
|
|
|
|
return CreateConstraint(constraintType, argumentString);
|
|
}
|
|
|
|
private static IRouteConstraint CreateConstraint(Type constraintType, string argumentString)
|
|
{
|
|
// No arguments - call the default constructor
|
|
if (argumentString == null)
|
|
{
|
|
return (IRouteConstraint)Activator.CreateInstance(constraintType);
|
|
}
|
|
|
|
var constraintTypeInfo = constraintType.GetTypeInfo();
|
|
ConstructorInfo activationConstructor = null;
|
|
object[] parameters = null;
|
|
var constructors = constraintTypeInfo.DeclaredConstructors.ToArray();
|
|
|
|
// If there is only one constructor and it has a single parameter, pass the argument string directly
|
|
// This is necessary for the Regex RouteConstraint to ensure that patterns are not split on commas.
|
|
if (constructors.Length == 1 && constructors[0].GetParameters().Length == 1)
|
|
{
|
|
activationConstructor = constructors[0];
|
|
parameters = ConvertArguments(activationConstructor.GetParameters(), new string[] { argumentString });
|
|
}
|
|
else
|
|
{
|
|
string[] arguments = argumentString.Split(',').Select(argument => argument.Trim()).ToArray();
|
|
|
|
var matchingConstructors = constructors.Where(ci => ci.GetParameters().Length == arguments.Length).ToArray();
|
|
var constructorMatches = matchingConstructors.Length;
|
|
|
|
if (constructorMatches == 0)
|
|
{
|
|
throw new InvalidOperationException(
|
|
Resources.FormatDefaultInlineConstraintResolver_CouldNotFindCtor(
|
|
constraintTypeInfo.Name, argumentString.Length));
|
|
}
|
|
else if (constructorMatches == 1)
|
|
{
|
|
activationConstructor = matchingConstructors[0];
|
|
parameters = ConvertArguments(activationConstructor.GetParameters(), arguments);
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException(
|
|
Resources.FormatDefaultInlineConstraintResolver_AmbiguousCtors(
|
|
constraintTypeInfo.Name, argumentString.Length));
|
|
}
|
|
}
|
|
|
|
return (IRouteConstraint)activationConstructor.Invoke(parameters);
|
|
}
|
|
|
|
private static object[] ConvertArguments(ParameterInfo[] parameterInfos, string[] arguments)
|
|
{
|
|
var parameters = new object[parameterInfos.Length];
|
|
for (int i = 0; i < parameterInfos.Length; i++)
|
|
{
|
|
var parameter = parameterInfos[i];
|
|
var parameterType = parameter.ParameterType;
|
|
parameters[i] = Convert.ChangeType(arguments[i], parameterType, CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
return parameters;
|
|
}
|
|
}
|
|
}
|