From 3e051754394e31880385aafa66ec92ba475c9291 Mon Sep 17 00:00:00 2001 From: Tebjan Halm Date: Fri, 29 Nov 2013 21:39:46 +0100 Subject: [PATCH] added a possibility to let the IdManager fix duplicated and wrong formated element ids --- Source/Exceptions/SvgException.cs | 24 +++++++++++++ Source/SvgElement.cs | 42 +++++++++++++++-------- Source/SvgElementCollection.cs | 11 +++++- Source/SvgElementIdManager.cs | 57 ++++++++++++++++++++++++++++--- 4 files changed, 114 insertions(+), 20 deletions(-) diff --git a/Source/Exceptions/SvgException.cs b/Source/Exceptions/SvgException.cs index 8a9f03f..240c319 100644 --- a/Source/Exceptions/SvgException.cs +++ b/Source/Exceptions/SvgException.cs @@ -11,4 +11,28 @@ namespace Svg { } } + + public class SvgIDException : FormatException + { + public SvgIDException(string message) + : base(message) + { + } + } + + public class SvgIDExistsException : SvgIDException + { + public SvgIDExistsException(string message) + : base(message) + { + } + } + + public class SvgIDWrongFormatException : SvgIDException + { + public SvgIDWrongFormatException(string message) + : base(message) + { + } + } } \ No newline at end of file diff --git a/Source/SvgElement.cs b/Source/SvgElement.cs index febac46..6f26915 100644 --- a/Source/SvgElement.cs +++ b/Source/SvgElement.cs @@ -259,26 +259,40 @@ namespace Svg get { return this.Attributes.GetAttribute("ID"); } set { - // Don't do anything if it hasn't changed - if (string.Compare(this.ID, value) == 0) - { - return; - } + SetAndFixID(value, false); + } + } - if (this.OwnerDocument != null) - { - this.OwnerDocument.IdManager.Remove(this); - } + public void SetAndFixID(string value, bool autoFixID = true, Action logElementOldIDNewID = null) + { + // Don't do anything if it hasn't changed + if (string.Compare(this.ID, value) == 0) + { + return; + } - this.Attributes["ID"] = value; + if (this.OwnerDocument != null) + { + this.OwnerDocument.IdManager.Remove(this); + } - if (this.OwnerDocument != null) - { - this.OwnerDocument.IdManager.Add(this); - } + this.Attributes["ID"] = value; + + if (this.OwnerDocument != null) + { + this.OwnerDocument.IdManager.AddAndFixID(this, autoFixID, logElementOldIDNewID); } } + /// + /// Only used by the ID Manager + /// + /// + internal void FixID(string newID) + { + this.Attributes["ID"] = newID; + } + /// /// Called by the underlying when an element has been added to the /// collection. diff --git a/Source/SvgElementCollection.cs b/Source/SvgElementCollection.cs index 0d3b35f..d69deaf 100644 --- a/Source/SvgElementCollection.cs +++ b/Source/SvgElementCollection.cs @@ -73,12 +73,21 @@ namespace Svg } public void Add(SvgElement item) + { + this.AddAndFixID(item, false, false); + } + + public void AddAndFixID(SvgElement item, bool autoFixID = true, bool autoFixChildrenID = true, Action logElementOldIDNewID = null) { if (!this._mock) { if (this._owner.OwnerDocument != null) { - item.ApplyRecursive(this._owner.OwnerDocument.IdManager.Add); + this._owner.OwnerDocument.IdManager.AddAndFixID(item, autoFixID, logElementOldIDNewID); + foreach (var child in item.Children) + { + child.ApplyRecursive(e => this._owner.OwnerDocument.IdManager.AddAndFixID(e, autoFixChildrenID, logElementOldIDNewID)); + } } item._parent = this._owner; diff --git a/Source/SvgElementIdManager.cs b/Source/SvgElementIdManager.cs index 2520f79..4dd803c 100644 --- a/Source/SvgElementIdManager.cs +++ b/Source/SvgElementIdManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; namespace Svg { @@ -47,13 +48,35 @@ namespace Svg /// The to be managed. public virtual void Add(SvgElement element) { + AddAndFixID(element, 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 AddAndFixID(SvgElement element, bool autoFixID = true, Action logElementOldIDNewID = null) + { + var result = false; if (!string.IsNullOrEmpty(element.ID)) { - this.EnsureValidId(element.ID); + var newID = this.EnsureValidId(element.ID, autoFixID); + if (autoFixID && newID != element.ID) + { + if(logElementOldIDNewID != null) + logElementOldIDNewID(element, element.ID, newID); + element.FixID(newID); + result = true; + } this._idValueMap.Add(element.ID, element); } OnAdded(element); + return result; } /// @@ -78,23 +101,47 @@ namespace Svg /// The ID cannot start with a digit. /// An element with the same ID already exists within the containing . /// - public void EnsureValidId(string id) + public string EnsureValidId(string id, bool autoFixID = false) { + if (string.IsNullOrEmpty(id)) { - return; + return id; } if (char.IsDigit(id[0])) { - throw new SvgException("ID cannot start with a digit: '" + id + "'."); + if (autoFixID) + { + return EnsureValidId("id" + id, true); + } + throw new SvgIDWrongFormatException("ID cannot start with a digit: '" + id + "'."); } if (this._idValueMap.ContainsKey(id)) { - throw new SvgException("An element with the same ID already exists: '" + id + "'."); + if(autoFixID) + { + 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 . -- GitLab