using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
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)
{
if (uri.ToString().StartsWith("url(")) uri = new Uri(uri.ToString().Substring(4).TrimEnd(')'), UriKind.Relative);
if (!uri.IsAbsoluteUri && this._document.BaseUri != null && !uri.ToString().StartsWith("#"))
{
var fullUri = new Uri(this._document.BaseUri, uri);
var hash = fullUri.OriginalString.Substring(fullUri.OriginalString.LastIndexOf('#'));
SvgDocument doc;
switch (fullUri.Scheme.ToLowerInvariant())
{
case "file":
doc = SvgDocument.Open(fullUri.LocalPath.Substring(0, fullUri.LocalPath.Length - hash.Length));
return doc.IdManager.GetElementById(hash);
case "http":
case "https":
var httpRequest = WebRequest.Create(uri);
using (WebResponse webResponse = httpRequest.GetResponse())
{
doc = SvgDocument.Open(webResponse.GetResponseStream());
return doc.IdManager.GetElementById(hash);
}
default:
throw new NotSupportedException();
}
}
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.
/// Not used.
/// 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;
}
}