maui-linux/Xamarin.Forms.Core/Routing.cs

222 строки
5.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Xamarin.Forms
{
public static class Routing
{
static int s_routeCount = 0;
static Dictionary<string, RouteFactory> s_routes = new Dictionary<string, RouteFactory>();
const string ImplicitPrefix = "IMPL_";
const string DefaultPrefix = "D_FAULT_";
const string _pathSeparator = "/";
internal static string GenerateImplicitRoute(string source)
{
if (IsImplicit(source))
return source;
return String.Concat(ImplicitPrefix, source);
}
internal static bool IsImplicit(string source)
{
return source.StartsWith(ImplicitPrefix, StringComparison.Ordinal);
}
internal static bool IsImplicit(BindableObject source)
{
return IsImplicit(GetRoute(source));
}
internal static bool IsDefault(string source)
{
return source.StartsWith(DefaultPrefix, StringComparison.Ordinal);
}
internal static bool IsDefault(BindableObject source)
{
return IsDefault(GetRoute(source));
}
internal static bool IsUserDefined(BindableObject source)
{
if (source == null)
return false;
return !(IsDefault(source) || IsImplicit(source));
}
internal static void Clear()
{
s_routes.Clear();
}
public static readonly BindableProperty RouteProperty =
BindableProperty.CreateAttached("Route", typeof(string), typeof(Routing), null,
defaultValueCreator: CreateDefaultRoute);
static object CreateDefaultRoute(BindableObject bindable)
{
return $"{DefaultPrefix}{bindable.GetType().Name}{++s_routeCount}";
}
internal static string[] GetRouteKeys()
{
string[] keys = new string[s_routes.Count];
s_routes.Keys.CopyTo(keys, 0);
return keys;
}
public static Element GetOrCreateContent(string route)
{
Element result = null;
if (s_routes.TryGetValue(route, out var content))
result = content.GetOrCreate();
if (result == null)
{
// okay maybe its a type, we'll try that just to be nice to the user
var type = Type.GetType(route);
if (type != null)
result = Activator.CreateInstance(type) as Element;
}
if (result != null)
SetRoute(result, route);
return result;
}
public static string GetRoute(BindableObject obj)
{
return (string)obj.GetValue(RouteProperty);
}
internal static string GetRoutePathIfNotImplicit(Element obj)
{
var source = GetRoute(obj);
if (IsImplicit(source))
return String.Empty;
return $"{source}/";
}
internal static Uri Remove(Uri uri, bool implicitRoutes, bool defaultRoutes)
{
uri = ShellUriHandler.FormatUri(uri, null);
string[] parts = uri.OriginalString.TrimEnd(_pathSeparator[0]).Split(_pathSeparator[0]);
bool userDefinedRouteAdded = false;
List<string> toKeep = new List<string>();
for (int i = 0; i < parts.Length; i++)
{
// This means there are no routes defined on the shell but the user has navigated to a global route
// so we need to attach the final route where the user left the shell
if (s_routes.ContainsKey(parts[i]) && !userDefinedRouteAdded && i > 0)
{
toKeep.Add(parts[i - 1]);
}
if (!(IsDefault(parts[i]) && defaultRoutes) && !(IsImplicit(parts[i]) && implicitRoutes))
{
if (!String.IsNullOrWhiteSpace(parts[i]))
userDefinedRouteAdded = true;
toKeep.Add(parts[i]);
}
}
if(!userDefinedRouteAdded && parts.Length > 0)
{
toKeep.Add(parts[parts.Length - 1]);
}
return new Uri(string.Join(_pathSeparator, toKeep), UriKind.Relative);
}
public static string FormatRoute(List<string> segments)
{
var route = FormatRoute(String.Join(_pathSeparator, segments));
return route;
}
public static string FormatRoute(string route)
{
return route;
}
public static void RegisterRoute(string route, RouteFactory factory)
{
if (!String.IsNullOrWhiteSpace(route))
route = FormatRoute(route);
ValidateRoute(route, factory);
s_routes[route] = factory;
}
public static void UnRegisterRoute(string route)
{
if (s_routes.TryGetValue(route, out _))
s_routes.Remove(route);
}
public static void RegisterRoute(string route, Type type)
{
RegisterRoute(route, new TypeRouteFactory(type));
}
public static void SetRoute(Element obj, string value)
{
obj.SetValue(RouteProperty, value);
}
static void ValidateRoute(string route, RouteFactory routeFactory)
{
if (string.IsNullOrWhiteSpace(route))
throw new ArgumentNullException(nameof(route), "Route cannot be an empty string");
routeFactory = routeFactory ?? throw new ArgumentNullException(nameof(routeFactory), "Route Factory cannot be null");
var uri = new Uri(route, UriKind.RelativeOrAbsolute);
var parts = uri.OriginalString.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var part in parts)
{
if (IsImplicit(part))
throw new ArgumentException($"Route contains invalid characters in \"{part}\"");
}
RouteFactory existingRegistration = null;
if(s_routes.TryGetValue(route, out existingRegistration) && !existingRegistration.Equals(routeFactory))
throw new ArgumentException($"Duplicated Route: \"{route}\"");
}
class TypeRouteFactory : RouteFactory
{
readonly Type _type;
public TypeRouteFactory(Type type)
{
_type = type;
}
public override Element GetOrCreate()
{
return (Element)Activator.CreateInstance(_type);
}
public override bool Equals(object obj)
{
if ((obj is TypeRouteFactory typeRouteFactory))
return typeRouteFactory._type == _type;
return false;
}
public override int GetHashCode()
{
return _type.GetHashCode();
}
}
}
}