using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace Svg { /// /// Provides methods to ensure element ID's are valid and unique. /// public class SvgElementIdManager { private SvgDocument _document; private Dictionary _idValueMap; /// /// Retrieves the with the specified ID. /// /// A containing the ID of the element to find. /// An of one exists with the specified ID; otherwise false. public virtual SvgElement GetElementById(string id) { if (id.StartsWith("url(")) { id = id.Substring(4); id = id.TrimEnd(')'); } if (id.StartsWith("#")) { id = id.Substring(1); } SvgElement element = null; this._idValueMap.TryGetValue(id, out element); return element; } public virtual SvgElement GetElementById(Uri uri) { return this.GetElementById(uri.ToString()); } /// /// Adds the specified for ID management. /// /// The to be managed. public virtual void Add(SvgElement element) { AddAndForceUniqueID(element, null, false); } /// /// Adds the specified for ID management. /// And can auto fix the ID if it already exists or it starts with a number. /// /// The to be managed. /// Pass true here, if you want the ID to be fixed /// If not null, the action is called before the id is fixed /// true, if ID was altered public virtual bool AddAndForceUniqueID(SvgElement element, SvgElement sibling, bool autoForceUniqueID = true, Action logElementOldIDNewID = null) { var result = false; if (!string.IsNullOrEmpty(element.ID)) { var newID = this.EnsureValidId(element.ID, autoForceUniqueID); if (autoForceUniqueID && newID != element.ID) { if(logElementOldIDNewID != null) logElementOldIDNewID(element, element.ID, newID); element.ForceUniqueID(newID); result = true; } this._idValueMap.Add(element.ID, element); } OnAdded(element); return result; } /// /// Removed the specified from ID management. /// /// The to be removed from ID management. public virtual void Remove(SvgElement element) { if (!string.IsNullOrEmpty(element.ID)) { this._idValueMap.Remove(element.ID); } OnRemoved(element); } /// /// Ensures that the specified ID is valid within the containing . /// /// A containing the ID to validate. /// Creates a new unique id . /// /// The ID cannot start with a digit. /// An element with the same ID already exists within the containing . /// public string EnsureValidId(string id, bool autoForceUniqueID = false) { if (string.IsNullOrEmpty(id)) { return id; } if (char.IsDigit(id[0])) { if (autoForceUniqueID) { return EnsureValidId("id" + id, true); } throw new SvgIDWrongFormatException("ID cannot start with a digit: '" + id + "'."); } if (this._idValueMap.ContainsKey(id)) { if(autoForceUniqueID) { var match = regex.Match(id); int number; if (match.Success && int.TryParse(match.Value.Substring(1), out number)) { id = regex.Replace(id, "#" + (number + 1)); } else { id += "#1"; } return EnsureValidId(id, true); } throw new SvgIDExistsException("An element with the same ID already exists: '" + id + "'."); } return id; } private static readonly Regex regex = new Regex(@"#\d+$"); /// /// Initialises a new instance of an . /// /// The containing the s to manage. public SvgElementIdManager(SvgDocument document) { this._document = document; this._idValueMap = new Dictionary(); } public event EventHandler ElementAdded; public event EventHandler ElementRemoved; protected void OnAdded(SvgElement element) { var handler = ElementAdded; if(handler != null) { handler(this._document, new SvgElementEventArgs{ Element = element }); } } protected void OnRemoved(SvgElement element) { var handler = ElementRemoved; if(handler != null) { handler(this._document, new SvgElementEventArgs{ Element = element }); } } } public class SvgElementEventArgs : EventArgs { public SvgElement Element; } }