using System;
using Avalonia.Media.Imaging;
using Avalonia.Metadata;
using Avalonia.Platform.Storage;
using Avalonia.Utilities;
namespace Avalonia.Input;
///
/// Represents a format usable with the clipboard and drag-and-drop.
///
public abstract class DataFormat : IEquatable
{
private protected DataFormat(DataFormatKind kind, string identifier)
{
Kind = kind;
Identifier = identifier;
}
///
/// Gets the kind of the data format.
///
public DataFormatKind Kind { get; }
///
/// Gets the identifier of the data format.
///
public string Identifier { get; }
///
/// Gets a data format representing plain text.
/// Its data type is .
///
public static DataFormat Text { get; } = CreateUniversalFormat("Text");
///
/// Gets a data format representing a bitmap.
/// Its data type is .
///
public static DataFormat Bitmap { get; } = CreateUniversalFormat("Bitmap");
///
/// Gets a data format representing a single file.
/// Its data type is .
///
public static DataFormat File { get; } = CreateUniversalFormat("File");
///
/// Creates a name for this format, usable by the underlying platform.
///
/// The system prefix used to recognize the name as an application format.
/// A system name for the format.
///
/// This method can only be called if is
/// or .
///
public string ToSystemName(string applicationPrefix)
{
ThrowHelper.ThrowIfNull(applicationPrefix);
return Kind switch
{
DataFormatKind.Application => applicationPrefix + Identifier,
DataFormatKind.Platform => Identifier,
_ => throw new InvalidOperationException($"Cannot get system name for universal format {Identifier}")
};
}
///
public bool Equals(DataFormat? other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
return Kind == other.Kind && Identifier == other.Identifier;
}
///
public override bool Equals(object? obj)
=> Equals(obj as DataFormat);
///
public override int GetHashCode()
=> ((int)Kind * 397) ^ Identifier.GetHashCode();
///
/// Compares two instances of for equality.
///
/// The first instance.
/// The second instance.
/// true if the two instances are equal; otherwise false.
public static bool operator ==(DataFormat? left, DataFormat? right)
=> Equals(left, right);
///
/// Compares two instances of for inequality.
///
/// The first instance.
/// The second instance.
/// true if the two instances are not equal; otherwise false.
public static bool operator !=(DataFormat? left, DataFormat? right)
=> !Equals(left, right);
private static DataFormat CreateUniversalFormat(string identifier) where T : class
=> new(DataFormatKind.Universal, identifier);
///
/// Creates a new format specific to the application that returns an array of .
///
///
///
/// The format identifier. To avoid conflicts with system identifiers, this value isn't passed to the underlying
/// platform directly. However, two different applications using the same identifier
/// with or
/// are able to share data using this format.
///
/// Only ASCII letters (A-Z, a-z), digits (0-9), the dot (.) and the hyphen (-) are accepted.
///
/// A new .
public static DataFormat CreateBytesApplicationFormat(string identifier)
=> CreateApplicationFormat(identifier);
///
/// Creates a new format specific to the application that returns a .
///
///
///
/// The format identifier. To avoid conflicts with system identifiers, this value isn't passed to the underlying
/// platform directly. However, two different applications using the same identifier
/// with or
/// are able to share data using this format.
///
/// Only ASCII letters (A-Z, a-z), digits (0-9), the dot (.) and the hyphen (-) are accepted.
///
/// A new .
public static DataFormat CreateStringApplicationFormat(string identifier)
=> CreateApplicationFormat(identifier);
private static DataFormat CreateApplicationFormat(string identifier)
where T : class
{
if (!IsValidApplicationFormatIdentifier(identifier))
throw new ArgumentException("Invalid application identifier", nameof(identifier));
return new(DataFormatKind.Application, identifier);
}
///
/// Creates a new format for the current platform that returns an array of .
///
///
/// The format identifier. This value is not validated and is passed AS IS to the underlying platform.
/// Most systems use mime types, but macOS requires Uniform Type Identifiers (UTI).
///
/// A new .
public static DataFormat CreateBytesPlatformFormat(string identifier)
=> CreatePlatformFormat(identifier);
///
/// Creates a new format for the current platform that returns a .
///
///
/// The format identifier. This value is not validated and is passed AS IS to the underlying platform.
/// Most systems use mime types, but macOS requires Uniform Type Identifiers (UTI).
///
/// A new .
public static DataFormat CreateStringPlatformFormat(string identifier)
=> CreatePlatformFormat(identifier);
private static DataFormat CreatePlatformFormat(string identifier)
where T : class
{
ThrowHelper.ThrowIfNullOrEmpty(identifier);
return new(DataFormatKind.Platform, identifier);
}
///
/// Creates a from a name coming from the underlying platform.
///
/// The name.
/// The system prefix used to recognize the name as an application format.
/// A corresponding to .
[PrivateApi]
public static DataFormat FromSystemName(string systemName, string applicationPrefix)
where T : class
{
ThrowHelper.ThrowIfNull(systemName);
ThrowHelper.ThrowIfNull(applicationPrefix);
if (systemName.StartsWith(applicationPrefix, StringComparison.OrdinalIgnoreCase))
{
var identifier = systemName.Substring(applicationPrefix.Length);
if (IsValidApplicationFormatIdentifier(identifier))
return new(DataFormatKind.Application, identifier);
}
return new(DataFormatKind.Platform, systemName);
}
private static bool IsValidApplicationFormatIdentifier(string identifier)
{
if (string.IsNullOrEmpty(identifier))
return false;
foreach (var c in identifier)
{
if (!IsValidChar(c))
return false;
}
return true;
static bool IsValidChar(char c)
=> char.IsAsciiLetterOrDigit(c) || c == '.' || c == '-';
}
///
public override string ToString()
=> $"{Kind}: {Identifier}";
}