164 lines
5.2 KiB
C#
164 lines
5.2 KiB
C#
// 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 System.Diagnostics;
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
namespace Microsoft.AspNetCore.Routing.Matching
|
|
{
|
|
// Use to sort and group Endpoints. RouteEndpoints are sorted before other implementations.
|
|
//
|
|
// NOTE:
|
|
// When ordering endpoints, we compare the route templates as an absolute last resort.
|
|
// This is used as a factor to ensure that we always have a predictable ordering
|
|
// for tests, errors, etc.
|
|
//
|
|
// When we group endpoints we don't consider the route template, because we're trying
|
|
// to group endpoints not separate them.
|
|
//
|
|
// TLDR:
|
|
// IComparer implementation considers the template string as a tiebreaker.
|
|
// IEqualityComparer implementation does not.
|
|
// This is cool and good.
|
|
internal class EndpointComparer : IComparer<Endpoint>, IEqualityComparer<Endpoint>
|
|
{
|
|
private readonly IComparer<Endpoint>[] _comparers;
|
|
|
|
public EndpointComparer(IEndpointComparerPolicy[] policies)
|
|
{
|
|
// Order, Precedence, (others)...
|
|
_comparers = new IComparer<Endpoint>[2 + policies.Length];
|
|
_comparers[0] = OrderComparer.Instance;
|
|
_comparers[1] = PrecedenceComparer.Instance;
|
|
for (var i = 0; i < policies.Length; i++)
|
|
{
|
|
_comparers[i + 2] = policies[i].Comparer;
|
|
}
|
|
}
|
|
|
|
public int Compare(Endpoint x, Endpoint y)
|
|
{
|
|
// We don't expose this publicly, and we should never call it on
|
|
// a null endpoint.
|
|
Debug.Assert(x != null);
|
|
Debug.Assert(y != null);
|
|
|
|
var compare = CompareCore(x, y);
|
|
|
|
// Since we're sorting, use the route template as a last resort.
|
|
return compare == 0 ? ComparePattern(x, y) : compare;
|
|
}
|
|
|
|
private int ComparePattern(Endpoint x, Endpoint y)
|
|
{
|
|
// A RouteEndpoint always comes before a non-RouteEndpoint, regardless of its RawText value
|
|
var routeEndpointX = x as RouteEndpoint;
|
|
var routeEndpointY = y as RouteEndpoint;
|
|
|
|
if (routeEndpointX != null)
|
|
{
|
|
if (routeEndpointY != null)
|
|
{
|
|
return routeEndpointX.RoutePattern.RawText.CompareTo(routeEndpointY.RoutePattern.RawText);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
else if (routeEndpointY != null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public bool Equals(Endpoint x, Endpoint y)
|
|
{
|
|
// We don't expose this publicly, and we should never call it on
|
|
// a null endpoint.
|
|
Debug.Assert(x != null);
|
|
Debug.Assert(y != null);
|
|
|
|
return CompareCore(x, y) == 0;
|
|
}
|
|
|
|
public int GetHashCode(Endpoint obj)
|
|
{
|
|
// This should not be possible to call publicly.
|
|
Debug.Fail("We don't expect this to be called.");
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
private int CompareCore(Endpoint x, Endpoint y)
|
|
{
|
|
for (var i = 0; i < _comparers.Length; i++)
|
|
{
|
|
var compare = _comparers[i].Compare(x, y);
|
|
if (compare != 0)
|
|
{
|
|
return compare;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private class OrderComparer : IComparer<Endpoint>
|
|
{
|
|
public static readonly IComparer<Endpoint> Instance = new OrderComparer();
|
|
|
|
public int Compare(Endpoint x, Endpoint y)
|
|
{
|
|
var routeEndpointX = x as RouteEndpoint;
|
|
var routeEndpointY = y as RouteEndpoint;
|
|
|
|
if (routeEndpointX != null)
|
|
{
|
|
if (routeEndpointY != null)
|
|
{
|
|
return routeEndpointX.Order.CompareTo(routeEndpointY.Order);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
else if (routeEndpointY != null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private class PrecedenceComparer : IComparer<Endpoint>
|
|
{
|
|
public static readonly IComparer<Endpoint> Instance = new PrecedenceComparer();
|
|
|
|
public int Compare(Endpoint x, Endpoint y)
|
|
{
|
|
var routeEndpointX = x as RouteEndpoint;
|
|
var routeEndpointY = y as RouteEndpoint;
|
|
|
|
if (routeEndpointX != null)
|
|
{
|
|
if (routeEndpointY != null)
|
|
{
|
|
return routeEndpointX.RoutePattern.InboundPrecedence
|
|
.CompareTo(routeEndpointY.RoutePattern.InboundPrecedence);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
else if (routeEndpointY != null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|