using System.ComponentModel; namespace Umbraco.Cms.Core; /// /// Represents an entity identifier. /// /// An Udi can be fully qualified or "closed" eg umb://document/{guid} or "open" eg umb://document. [TypeConverter(typeof(UdiTypeConverter))] public abstract class Udi : IComparable { /// /// Initializes a new instance of the Udi class. /// /// The entity type part of the identifier. /// The string value of the identifier. protected Udi(string entityType, string stringValue) { EntityType = entityType; UriValue = new Uri(stringValue); } /// /// Initializes a new instance of the Udi class. /// /// The uri value of the identifier. protected Udi(Uri uriValue) { EntityType = uriValue.Host; UriValue = uriValue; } public Uri UriValue { get; } /// /// Gets the entity type part of the identifier. /// public string EntityType { get; } /// /// Gets a value indicating whether this Udi is a root Udi. /// /// A root Udi points to the "root of all things" for a given entity type, e.g. the content tree root. public abstract bool IsRoot { get; } public static bool operator ==(Udi? udi1, Udi? udi2) { if (ReferenceEquals(udi1, udi2)) { return true; } if (udi1 is null || udi2 is null) { return false; } return udi1.Equals(udi2); } /// /// Creates a root Udi for an entity type. /// /// The entity type. /// The root Udi for the entity type. public static Udi Create(string entityType) => UdiParser.GetRootUdi(entityType); public int CompareTo(Udi? other) => string.Compare(UriValue.ToString(), other?.UriValue.ToString(), StringComparison.OrdinalIgnoreCase); public override string ToString() => // UriValue is created in the ctor and is never null // use AbsoluteUri here and not ToString else it's not encoded! UriValue.AbsoluteUri; /// /// Creates a string Udi. /// /// The entity type. /// The identifier. /// The string Udi for the entity type and identifier. public static Udi Create(string entityType, string id) { if (UdiParser.UdiTypes.TryGetValue(entityType, out UdiType udiType) == false) { throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType"); } if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("Value cannot be null or whitespace.", "id"); } if (udiType != UdiType.StringUdi) { throw new InvalidOperationException(string.Format( "Entity type \"{0}\" does not have string udis.", entityType)); } return new StringUdi(entityType, id); } /// /// Creates a Guid Udi. /// /// The entity type. /// The identifier. /// The Guid Udi for the entity type and identifier. public static Udi Create(string? entityType, Guid id) { if (entityType is null || UdiParser.UdiTypes.TryGetValue(entityType, out UdiType udiType) == false) { throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType"); } if (udiType != UdiType.GuidUdi) { throw new InvalidOperationException(string.Format( "Entity type \"{0}\" does not have guid udis.", entityType)); } if (id == default) { throw new ArgumentException("Cannot be an empty guid.", "id"); } return new GuidUdi(entityType, id); } public static Udi Create(Uri uri) { // if it's a know type go fast and use ctors // else fallback to parsing the string (and guess the type) if (UdiParser.UdiTypes.TryGetValue(uri.Host, out UdiType udiType) == false) { throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", uri.Host), "uri"); } if (udiType == UdiType.GuidUdi) { return new GuidUdi(uri); } if (udiType == UdiType.StringUdi) { return new StringUdi(uri); } throw new ArgumentException(string.Format("Uri \"{0}\" is not a valid udi.", uri)); } public void EnsureType(params string[] validTypes) { if (validTypes.Contains(EntityType) == false) { throw new Exception(string.Format("Unexpected entity type \"{0}\".", EntityType)); } } /// /// Ensures that this Udi is not a root Udi. /// /// This Udi. /// When this Udi is a Root Udi. public Udi EnsureNotRoot() { if (IsRoot) { throw new Exception("Root Udi."); } return this; } public override bool Equals(object? obj) { var other = obj as Udi; return other is not null && GetType() == other.GetType() && UriValue == other.UriValue; } public override int GetHashCode() => UriValue.GetHashCode(); public static bool operator !=(Udi? udi1, Udi? udi2) => udi1 == udi2 == false; }