using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace OpenTap
{
///
/// A mechanism for retrieving data with
///
internal interface IPictureDataProvider
{
///
/// The order in which IPictureDataProviders will be tested. Lowers numbers go first
///
double Order { get; }
///
/// Get a stream of the picture data
///
///
///
Task GetStream(IPicture picture);
///
/// Get a string specifying the picture format
///
///
///
Task GetFormat(IPicture picture);
}
///
/// Picture data provider for URI sources.
///
class DefaultPictureDataProvider : IPictureDataProvider
{
public double Order => 10;
///
/// Relative URIs are poorly supported in dotnet core. Ensure we only use absolute URIs by normalizing path strings to absolute paths.
///
///
///
private string normalizeSource(string source)
{
try
{
if (File.Exists(source))
return Path.GetFullPath(source);
}
catch
{
// this is fine -- source is not a file
}
return source;
}
public async Task GetStream(IPicture picture)
{
if (await GetFormat(picture) == null) return null;
var source = normalizeSource(picture.Source);
if (Uri.TryCreate(source, UriKind.Absolute, out var uri))
{
#pragma warning disable SYSLIB0014
var req = WebRequest.Create(uri);
#pragma warning restore SYSLIB0014
var response = await req.GetResponseAsync();
return response.GetResponseStream();
}
return null;
}
public Task GetFormat(IPicture picture)
{
var source = normalizeSource(picture.Source);
if (Uri.TryCreate(source, UriKind.Absolute, out var uri))
{
var name = uri.Segments.LastOrDefault();
if (string.IsNullOrWhiteSpace(name) == false)
{
var ext = Path.GetExtension(name);
if (ext.StartsWith(".") && ext.Length > 1) return Task.FromResult(ext.Substring(1));
}
}
return Task.FromResult(null);
}
}
///
/// Provide data from implementations.
///
public static class PictureDataExtensions
{
private static IPictureDataProvider[] cache = Array.Empty();
private static ISet cacheKey;
private static IEnumerable GetProviders()
{
var types = TypeData.GetDerivedTypes()
.Where(td => td.CanCreateInstance).ToImmutableHashSet();
if (cacheKey == null || types.SetEquals(cacheKey) == false)
{
cache = types.TrySelect(td => td.CreateInstance(), ex => log.Debug("Unable to load IPictureDataProvider: {0}", ex.Message))
.OfType().OrderBy(p => p.Order)
.ToArray();
cacheKey = types;
}
return cache;
}
private static TraceSource log = Log.CreateSource(nameof(PictureDataExtensions));
private static async Task GetFirst(IPicture picture, Func> func) where T : class
{
foreach (var provider in GetProviders())
{
try
{
var res = await func(picture, provider);
if (res != null)
return res;
}
catch (Exception ex)
{
log.Debug($"Unexpected error in {nameof(IPictureDataProvider)} '{TypeData.GetTypeData(provider).AsTypeData().GetBestName()}'.");
log.Debug(ex);
}
}
return null;
}
///
/// Get a stream of the picture from the first which returns a non-null.
///
///
public static Task GetStream(this IPicture picture) =>
GetFirst(picture, (pic, provider) => provider.GetStream(pic));
///
/// Get the format of the picture from the first which returns a non-null.
///
///
public static Task GetFormat(this IPicture picture) => GetFirst(picture, (pic, provider) => provider.GetFormat(pic));
}
}