// 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.Collections.Generic;
using System.Diagnostics.Contracts;
namespace System.Web.Http.Routing
{
public static class HttpRouteDataExtensions
{
///
/// Remove all optional parameters that do not have a value from the route data.
///
/// route data, to be mutated in-place.
public static void RemoveOptionalRoutingParameters(this IHttpRouteData routeData)
{
RemoveOptionalRoutingParameters(routeData.Values);
var subRouteData = routeData.GetSubRoutes();
if (subRouteData != null)
{
foreach (IHttpRouteData sub in subRouteData)
{
RemoveOptionalRoutingParameters(sub);
}
}
}
private static void RemoveOptionalRoutingParameters(IDictionary routeValueDictionary)
{
Contract.Assert(routeValueDictionary != null);
// Get all keys for which the corresponding value is 'Optional'.
// Having a separate array is necessary so that we don't manipulate the dictionary while enumerating.
// This is on a hot-path and linq expressions are showing up on the profile, so do array manipulation.
int max = routeValueDictionary.Count;
int i = 0;
string[] matching = new string[max];
foreach (KeyValuePair kv in routeValueDictionary)
{
if (kv.Value == RouteParameter.Optional)
{
matching[i] = kv.Key;
i++;
}
}
for (int j = 0; j < i; j++)
{
string key = matching[j];
routeValueDictionary.Remove(key);
}
}
///
/// If a route is really a union of other routes, return the set of sub routes.
///
/// a union route data
/// set of sub soutes contained within this route
public static IEnumerable GetSubRoutes(this IHttpRouteData routeData)
{
IHttpRouteData[] subRoutes = null;
if (routeData.Values.TryGetValue(RouteCollectionRoute.SubRouteDataKey, out subRoutes))
{
return subRoutes;
}
return null;
}
// If routeData is from an attribute route, get the action descriptors, order and precedence that it may match
// to. Caller still needs to run action selection to pick the specific action.
// Else return null.
internal static CandidateAction[] GetDirectRouteCandidates(this IHttpRouteData routeData)
{
Contract.Assert(routeData != null);
IEnumerable subRoutes = routeData.GetSubRoutes();
// Possible this is being called on a subroute. This can happen after ElevateRouteData. Just chain.
if (subRoutes == null)
{
if (routeData.Route == null)
{
// If the matched route is a System.Web.Routing.Route (in web host) then routeData.Route
// will be null. Normally a System.Web.Routing.Route match would go through an MVC handler
// but we can get here through HttpRoutingDispatcher in WebAPI batching. If that happens,
// then obviously it's not a WebAPI attribute routing match.
return null;
}
else
{
return routeData.Route.GetDirectRouteCandidates();
}
}
var list = new List();
foreach (IHttpRouteData subData in subRoutes)
{
CandidateAction[] candidates = subData.Route.GetDirectRouteCandidates();
if (candidates != null)
{
list.AddRange(candidates);
}
}
return list.ToArray();
}
}
}