A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

222 lines
8.2 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Avalonia.Platform;
using Avalonia.PlatformSupport.Internal;
using Avalonia.Utilities;
namespace Avalonia.PlatformSupport
{
/// <summary>
/// Loads assets compiled into the application binary.
/// </summary>
public class AssetLoader : IAssetLoader
{
private static IAssemblyDescriptorResolver s_assemblyDescriptorResolver = new AssemblyDescriptorResolver();
private AssemblyDescriptor? _defaultResmAssembly;
/// <remarks>
/// Introduced for tests.
/// </remarks>
internal static void SetAssemblyDescriptorResolver(IAssemblyDescriptorResolver resolver) =>
s_assemblyDescriptorResolver = resolver;
/// <summary>
/// Initializes a new instance of the <see cref="AssetLoader"/> class.
/// </summary>
/// <param name="assembly">
/// The default assembly from which to load resm: assets for which no assembly is specified.
/// </param>
public AssetLoader(Assembly? assembly = null)
{
if (assembly == null)
assembly = Assembly.GetEntryAssembly();
if (assembly != null)
_defaultResmAssembly = new AssemblyDescriptor(assembly);
}
/// <summary>
/// Sets the default assembly from which to load assets for which no assembly is specified.
/// </summary>
/// <param name="assembly">The default assembly.</param>
public void SetDefaultAssembly(Assembly assembly)
{
_defaultResmAssembly = new AssemblyDescriptor(assembly);
}
/// <summary>
/// Checks if an asset with the specified URI exists.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>True if the asset could be found; otherwise false.</returns>
public bool Exists(Uri uri, Uri? baseUri = null)
{
return GetAsset(uri, baseUri) != null;
}
/// <summary>
/// Opens the asset with the requested URI.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>A stream containing the asset contents.</returns>
/// <exception cref="FileNotFoundException">
/// The asset could not be found.
/// </exception>
public Stream Open(Uri uri, Uri? baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1;
/// <summary>
/// Opens the asset with the requested URI and returns the asset stream and the
/// assembly containing the asset.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>
/// The stream containing the resource contents together with the assembly.
/// </returns>
/// <exception cref="FileNotFoundException">
/// The asset could not be found.
/// </exception>
public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null)
{
var asset = GetAsset(uri, baseUri);
if (asset == null)
{
throw new FileNotFoundException($"The resource {uri} could not be found.");
}
return (asset.GetStream(), asset.Assembly);
}
public Assembly? GetAssembly(Uri uri, Uri? baseUri)
{
if (!uri.IsAbsoluteUri && baseUri != null)
uri = new Uri(baseUri, uri);
return GetAssembly(uri)?.Assembly;
}
/// <summary>
/// Gets all assets of a folder and subfolders that match specified uri.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">Base URI that is used if <paramref name="uri"/> is relative.</param>
/// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
public IEnumerable<Uri> GetAssets(Uri uri, Uri? baseUri)
{
if (uri.IsAbsoluteResm())
{
var assembly = GetAssembly(uri);
return assembly?.Resources?
.Where(x => x.Key.IndexOf(uri.GetUnescapeAbsolutePath(), StringComparison.Ordinal) >= 0)
.Select(x =>new Uri($"resm:{x.Key}?assembly={assembly.Name}")) ??
Enumerable.Empty<Uri>();
}
uri = uri.EnsureAbsolute(baseUri);
if (uri.IsAvares())
{
var (asm, path) = GetResAsmAndPath(uri);
if (asm == null)
{
throw new ArgumentException(
"No default assembly, entry assembly or explicit assembly specified; " +
"don't know where to look up for the resource, try specifying assembly explicitly.");
}
if (asm.AvaloniaResources == null)
return Enumerable.Empty<Uri>();
if (path[path.Length - 1] != '/')
path += '/';
return asm.AvaloniaResources
.Where(r => r.Key.StartsWith(path, StringComparison.Ordinal))
.Select(x => new Uri($"avares://{asm.Name}{x.Key}"));
}
return Enumerable.Empty<Uri>();
}
private IAssetDescriptor? GetAsset(Uri uri, Uri? baseUri)
{
if (uri.IsAbsoluteResm())
{
var asm = GetAssembly(uri) ?? GetAssembly(baseUri) ?? _defaultResmAssembly;
if (asm == null)
{
throw new ArgumentException(
"No default assembly, entry assembly or explicit assembly specified; " +
"don't know where to look up for the resource, try specifying assembly explicitly.");
}
var resourceKey = uri.AbsolutePath;
IAssetDescriptor? rv = null;
asm.Resources?.TryGetValue(resourceKey, out rv);
return rv;
}
uri = uri.EnsureAbsolute(baseUri);
if (uri.IsAvares())
{
var (asm, path) = GetResAsmAndPath(uri);
if (asm.AvaloniaResources == null)
return null;
asm.AvaloniaResources.TryGetValue(path, out var desc);
return desc;
}
throw new ArgumentException($"Unsupported url type: " + uri.Scheme, nameof(uri));
}
private (IAssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri)
{
var asm = s_assemblyDescriptorResolver.GetAssembly(uri.Authority);
return (asm, uri.GetUnescapeAbsolutePath());
}
private IAssemblyDescriptor? GetAssembly(Uri? uri)
{
if (uri != null)
{
if (!uri.IsAbsoluteUri)
return null;
if (uri.IsAvares())
return GetResAsmAndPath(uri).asm;
if (uri.IsResm())
{
var assemblyName = uri.GetAssemblyNameFromQuery();
if (assemblyName.Length > 0)
return s_assemblyDescriptorResolver.GetAssembly(assemblyName);
}
}
return null;
}
public static void RegisterResUriParsers()
{
if (!UriParser.IsKnownScheme("avares"))
UriParser.Register(new GenericUriParser(
GenericUriParserOptions.GenericAuthority |
GenericUriParserOptions.NoUserInfo |
GenericUriParserOptions.NoPort |
GenericUriParserOptions.NoQuery |
GenericUriParserOptions.NoFragment), "avares", -1);
}
}
}