using System; using System.Text; using System.Collections.Generic; namespace Wayne.Lib { /// /// The IIdentifiableEntity represents an entity of some sort that has an integer ID and a possible parent. /// public interface IIdentifiableEntity { #region Properties /// /// The ID of the entity. /// int Id { get; } /// /// The main type of entity. /// string EntityType { get; } /// /// This is used by the logger and should never be set by inheriting classes /// string FullEntityName { get; set; } /// /// A more refined type of the entity, e.g. a specific implementation or brand. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "SubType")] string EntitySubType { get; } /// /// Reference to a possible parent device. /// IIdentifiableEntity ParentEntity { get; } #endregion } /// /// A class implementing the IIdentifiableEntity interface. /// Also contains static properties and methods that handles IIdentifiableEntity. /// public class IdentifiableEntity : IIdentifiableEntity { #region Fields private readonly int id; private readonly string entityType; private readonly string entitySubType; private readonly IIdentifiableEntity parentEntity; #endregion #region Construction static IdentifiableEntity() { Empty = new IdentifiableEntity(NoId, string.Empty, string.Empty, null); } /// /// Construction /// /// The ID of the entity. /// The main type of entity. /// A more refined type of the entity, e.g. a specific implementation or brand. /// Reference to a possible parent device. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "SubType")] public IdentifiableEntity(int id, string entityType, string entitySubType, IIdentifiableEntity parentEntity) { this.id = id; this.entityType = entityType; this.entitySubType = entitySubType; this.parentEntity = parentEntity; } #endregion #region IIdentifiableEntity Members /// /// The ID of the entity. /// public int Id { get { return id; } } /// /// The main type of entity. /// public string EntitySubType { get { return entitySubType; } } /// /// A more refined type of the entity, e.g. a specific implementation or brand. /// public string EntityType { get { return entityType; } } /// /// This is used by the logger and should never be set by inheriting classes /// public string FullEntityName { get; set; } /// /// Reference to a possible parent device. /// public IIdentifiableEntity ParentEntity { get { return parentEntity; } } #endregion #region Misc Static Members /// /// A value representing a non-Id. /// public const int NoId = int.MinValue; /// /// An empty IdentifiableEntity. /// public static IdentifiableEntity Empty { get; private set; } /// /// Gets an IIdentifiableEntity-array of the ancestors of the given entity. /// The first one in the list is the given entity itself, and the last one is the root-parent. /// /// /// public static IIdentifiableEntity[] GetAncestorArray(IIdentifiableEntity entity) { List ancestors = new List(); while (entity != null) { ancestors.Add(entity); entity = entity.ParentEntity; } return ancestors.ToArray(); } /// /// Tests equality between two identifieable entities on the regards on the /// Id, EntityType, EntitySubtype, and the parent ancestry. /// /// /// /// public static bool Equals(IIdentifiableEntity entity1, IIdentifiableEntity entity2) { if (entity1 == null) throw new ArgumentNullException("entity1"); if (entity2 == null) throw new ArgumentNullException("entity2"); if (entity1 == entity2) return true; EnsureFullEntityName(entity1); EnsureFullEntityName(entity2); return entity1.FullEntityName == entity2.FullEntityName; } private static void EnsureFullEntityName(IIdentifiableEntity entity1) { if (string.IsNullOrEmpty(entity1.FullEntityName)) { entity1.FullEntityName = ToString(entity1, true); } } #endregion #region ToString /// /// Composes a string from this IIdentifiableEntity. /// /// public override string ToString() { if (string.IsNullOrEmpty(FullEntityName)) { FullEntityName = ToString(this, true); } return FullEntityName; } /// /// Composes a string from an IIdentifiableEntity. /// /// The entity to convert to a string. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")] public static string ToString(IIdentifiableEntity entity) { return ToString(entity, false); } /// /// Composes a string from an IIdentifiableEntity, possibly with the ancestors included. /// /// The entity to convert to a string. /// Should the ancestors be included? /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")] public static string ToString(IIdentifiableEntity entity, bool ancestors) { if ((entity == null) || (entity == Empty)) return string.Empty; StringBuilder builder = new StringBuilder(); if (ancestors) { List entities = new List(); do { // The EntityType builder.Append(entity.EntityType); // The EntitySubType if (!string.IsNullOrEmpty(entity.EntitySubType)) { builder.Append("{"); builder.Append(entity.EntitySubType); builder.Append("}"); } // The Id if (entity.Id != NoId) { builder.Append('#'); builder.Append(entity.Id.ToString(System.Globalization.CultureInfo.InvariantCulture)); } entity = entity.ParentEntity; entities.Add(builder.ToString()); builder.Length = 0; } while (entity != null); for (int i = entities.Count - 1; i >= 0; i--) { builder.Append("\\"); builder.Append(entities[i]); } } else { // The EntityType builder.Append(entity.EntityType); // The EntitySubType if (!string.IsNullOrEmpty(entity.EntitySubType)) { builder.Append("{"); builder.Append(entity.EntitySubType); builder.Append("}"); } // The Id if (entity.Id != NoId) builder.Append(entity.Id.ToString(System.Globalization.CultureInfo.InvariantCulture)); } return builder.ToString(); } #endregion #region Parse /// /// Parses an Ancestor string into a list of Identifiable entities, where the first item is the bottommost parent, /// /// /// public static IIdentifiableEntity[] ParseAncestorString(string ancestryString) { string[] entityStrings = ancestryString.Split('\\'); List identifiableEntityList = new List(); IIdentifiableEntity currentParent = null; System.Text.RegularExpressions.Regex allowedEntityStringFormat = new System.Text.RegularExpressions.Regex(@"^[a-zA-Z0-9_\.]+({[a-zA-Z0-9_\.]+})?(#[0-9]+)?$"); foreach (string entityString in entityStrings) { if (string.IsNullOrEmpty(entityString)) continue; if (!allowedEntityStringFormat.IsMatch(entityString)) return new IIdentifiableEntity[0]; string entityType; string entitySubType = string.Empty; int id = NoId; //Entity string can have the following formats: // 1. Entity // 2. Entity{SubType} // 3. Entity{SubType}#Id // 4. Entity#Id string[] firstSplit = entityString.Split('#'); if (firstSplit.Length >= 1) { //Is it case 1 or 2? List secondSplit = new List(firstSplit[0].Split('{', '}')); while (secondSplit.Contains(string.Empty)) secondSplit.Remove(string.Empty); if (secondSplit.Count == 1) { //Was Case 1 entityType = secondSplit[0]; } else if (secondSplit.Count == 2) { //Was Case 2 entityType = secondSplit[0]; entitySubType = secondSplit[1]; } else return new IIdentifiableEntity[0]; if (firstSplit.Length == 2) id = int.Parse(firstSplit[1], System.Globalization.CultureInfo.InvariantCulture); else if (firstSplit.Length > 2) return new IIdentifiableEntity[0]; } else return new IIdentifiableEntity[0]; IIdentifiableEntity entity = new IdentifiableEntity(id, entityType, entitySubType, currentParent); identifiableEntityList.Add(entity); currentParent = entity; } return identifiableEntityList.ToArray(); } /// /// Parses an ancestry string entity into an IdentifiableEntity object whith newly created Identifiable entities for parents. /// /// /// public static IIdentifiableEntity Parse(string path) { System.Text.RegularExpressions.Regex allowedEntityStringFormat = new System.Text.RegularExpressions.Regex(@"^[a-zA-Z0-9_\.]+({[a-zA-Z0-9_\.]+})?(#[0-9]+)?$"); string[] entityStrings = path.TrimStart('\\').Split('\\'); IdentifiableEntity currentParent = null; foreach (string entityString in entityStrings) { if (!allowedEntityStringFormat.IsMatch(entityString)) return null; string entityType; string entitySubType = string.Empty; int id = NoId; //Entity string can have the following formats: // 1. Entity // 2. Entity{SubType} // 3. Entity{SubType}#Id // 4. Entity#Id string[] firstSplit = entityString.Split('#'); if (firstSplit.Length >= 1) { //Is it case 1 or 2? List secondSplit = new List(firstSplit[0].Split('{', '}')); while (secondSplit.Contains(string.Empty)) secondSplit.Remove(string.Empty); if (secondSplit.Count == 1) { //Was Case 1 entityType = secondSplit[0]; } else if (secondSplit.Count == 2) { //Was Case 2 entityType = secondSplit[0]; entitySubType = secondSplit[1]; } else return null; if (firstSplit.Length == 2) id = int.Parse(firstSplit[1], System.Globalization.CultureInfo.InvariantCulture); else if (firstSplit.Length > 2) return null; } else return null; IdentifiableEntity entity = new IdentifiableEntity(id, entityType, entitySubType, currentParent); currentParent = entity; } return currentParent; } public class EqualityComparer : IEqualityComparer { public bool Equals(IIdentifiableEntity x, IIdentifiableEntity y) { return IdentifiableEntity.Equals(x, y); } public int GetHashCode(IIdentifiableEntity obj) { EnsureFullEntityName(obj); return obj.FullEntityName.GetHashCode(); } } #endregion } }