using System; using System.Linq; using System.Reflection; namespace WebViewControl { public partial class ResourceUrl { public const string LocalScheme = "local"; public const string CustomScheme = "custom"; internal const string EmbeddedScheme = "embedded"; internal const string PathSeparator = "/"; private const string AssemblyPathSeparator = ";"; private const char AssemblyVersionSeparator = '-'; private const string AssemblyPrefix = "assembly:"; private const string DefaultDomain = "webview{0}"; private string Url { get; } public ResourceUrl(params string[] path) { Url = string.Join("/", path); } public ResourceUrl(Assembly assembly, params string[] path) : this(path) { var identity = assembly.GetName(); var assemblyName = identity.Name; var assemblyVersion = identity.Version is { } version ? $"{AssemblyVersionSeparator}{version}" : ""; if (Url.StartsWith(PathSeparator)) { // only prefix with assembly if necessary, to avoid having the same resource loaded from multiple locations Url = AssemblyPrefix + assemblyName + assemblyVersion + AssemblyPathSeparator + Url.Substring(1); } else { Url = assemblyName + assemblyVersion + PathSeparator + Url; } Url = BuildUrl(EmbeddedScheme, Url); } internal ResourceUrl(string scheme, string path) { Url = BuildUrl(scheme, path); } private static string BuildUrl(string scheme, string path) { return scheme + Uri.SchemeDelimiter + CombinePath(DefaultDomain, path); } private static string CombinePath(string path1, string path2) { return path1 + (path1.EndsWith(PathSeparator) ? "" : PathSeparator) + (path2.StartsWith(PathSeparator) ? path2.Substring(1) : path2); } public override string ToString() { return string.Format(Url, ""); } private static bool ContainsAssemblyLocation(Uri url) { return url.Scheme == EmbeddedScheme && url.AbsolutePath.StartsWith(PathSeparator + AssemblyPrefix); } /// /// Supported syntax: /// embedded://webview/assembly:AssemblyName;Path/To/Resource /// embedded://webview/assembly:AssemblyName-AssemblyVersion;Path/To/Resource /// embedded://webview/AssemblyName/Path/To/Resource (AssemblyName is also assumed as default namespace) /// embedded://webview/AssemblyName-AssemblyVersion/Path/To/Resource /// internal static string[] GetEmbeddedResourcePath(Uri resourceUrl) { if (ContainsAssemblyLocation(resourceUrl)) { var indexOfPath = resourceUrl.AbsolutePath.IndexOf(AssemblyPathSeparator); return resourceUrl.AbsolutePath.Substring(indexOfPath + 1).Split(new [] { PathSeparator }, StringSplitOptions.None); } var uriParts = resourceUrl.Segments.Select(p => p.Replace(PathSeparator, "")).ToArray(); var (assemblyName, _) = GetAssemblyNameAndVersion(uriParts[1]); return uriParts.Skip(2).Prepend(assemblyName).ToArray(); } /// /// Supported syntax: /// embedded://webview/assembly:AssemblyName;Path/To/Resource /// embedded://webview/assembly:AssemblyName-AssemblyVersion;Path/To/Resource /// embedded://webview/AssemblyName/Path/To/Resource (AssemblyName is also assumed as default namespace) /// embedded://webview/AssemblyName-AssemblyVersion/Path/To/Resource /// public static (string, Version) GetEmbeddedResourceAssemblyNameAndVersion(Uri resourceUrl) { if (ContainsAssemblyLocation(resourceUrl)) { var resourcePath = resourceUrl.AbsolutePath.Substring((PathSeparator + AssemblyPrefix).Length); var indexOfPath = Math.Max(0, resourcePath.IndexOf(AssemblyPathSeparator)); return GetAssemblyNameAndVersion(resourcePath.Substring(0, indexOfPath)); } if (resourceUrl.Segments.Length > 1) { var assemblySegment = resourceUrl.Segments[1]; // default assembly name to the first path return GetAssemblyNameAndVersion(assemblySegment.EndsWith(PathSeparator) ? assemblySegment.Substring(0, assemblySegment.Length - PathSeparator.Length) : assemblySegment); } return (string.Empty, null); } private static (string, Version) GetAssemblyNameAndVersion(string assemblyNameAndVersion) { var parts = assemblyNameAndVersion.Split(AssemblyVersionSeparator); return parts.Length == 2 ? (parts[0], new Version(parts[1])) : (parts[0], null); } internal string WithDomain(string domain) { return string.Format(Url, string.IsNullOrEmpty(domain) ? "" : ("_" + domain)); } } }