using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
namespace Svg
{
///
/// Provides the methods required in order to parse and create instances from XML.
///
internal class SvgElementFactory
{
private static List availableElements;
///
/// Gets a list of available types that can be used when creating an .
///
private static List AvailableElements
{
get
{
if (availableElements == null)
{
var svgTypes = from t in typeof(SvgDocument).Assembly.GetExportedTypes()
where t.GetCustomAttributes(typeof(SvgElementAttribute), true).Length > 0
&& t.IsSubclassOf(typeof(SvgElement))
select new ElementInfo { ElementName = ((SvgElementAttribute)t.GetCustomAttributes(typeof(SvgElementAttribute), true)[0]).ElementName, ElementType = t };
availableElements = svgTypes.ToList();
}
return availableElements;
}
}
///
/// Creates an from the current node in the specified .
///
/// The containing the node to parse into an .
/// The parameter cannot be null .
/// The CreateDocument method can only be used to parse root <svg> elements.
public static T CreateDocument(XmlTextReader reader) where T : SvgDocument, new()
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
if (reader.LocalName != "svg")
{
throw new InvalidOperationException("The CreateDocument method can only be used to parse root elements.");
}
return (T)CreateElement(reader, true, null);
}
///
/// Creates an from the current node in the specified .
///
/// The containing the node to parse into a subclass of .
/// The that the created element belongs to.
/// The and parameters cannot be null .
public static SvgElement CreateElement(XmlTextReader reader, SvgDocument document)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
return CreateElement(reader, false, document);
}
private static SvgElement CreateElement(XmlTextReader reader, bool fragmentIsDocument, SvgDocument document) where T : SvgDocument, new()
{
SvgElement createdElement = null;
string elementName = reader.LocalName;
//Trace.TraceInformation("Begin CreateElement: {0}", elementName);
if (elementName == "svg")
{
createdElement = (fragmentIsDocument) ? new T() : new SvgFragment();
}
else
{
ElementInfo validType = AvailableElements.SingleOrDefault(e => e.ElementName == elementName);
if (validType != null)
{
createdElement = (SvgElement)Activator.CreateInstance(validType.ElementType);
}
}
if (createdElement != null)
{
SetAttributes(createdElement, reader, document);
}
//Trace.TraceInformation("End CreateElement");
return createdElement;
}
private static void SetAttributes(SvgElement element, XmlTextReader reader, SvgDocument document)
{
//Trace.TraceInformation("Begin SetAttributes");
string[] styles = null;
string[] style = null;
int i = 0;
while (reader.MoveToNextAttribute())
{
// Special treatment for "style"
if (reader.LocalName.Equals("style"))
{
styles = reader.Value.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
for (i = 0; i < styles.Length; i++)
{
if (!styles[i].Contains(":"))
{
continue;
}
style = styles[i].Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
SetPropertyValue(element, style[0].Trim(), style[1].Trim(), document);
}
continue;
}
SetPropertyValue(element, reader.LocalName, reader.Value, document);
}
//Trace.TraceInformation("End SetAttributes");
}
private static Dictionary> _propertyDescriptors = new Dictionary>();
private static void SetPropertyValue(SvgElement element, string attributeName, string attributeValue, SvgDocument document)
{
var elementType = element.GetType();
PropertyDescriptorCollection properties;
if (_propertyDescriptors.Keys.Contains(elementType))
{
if (_propertyDescriptors[elementType].Keys.Contains(attributeName))
{
properties = _propertyDescriptors[elementType][attributeName];
}
else
{
properties = TypeDescriptor.GetProperties(elementType, new[] { new SvgAttributeAttribute(attributeName) });
_propertyDescriptors[elementType].Add(attributeName, properties);
}
}
else
{
properties = TypeDescriptor.GetProperties(elementType, new[] { new SvgAttributeAttribute(attributeName) });
_propertyDescriptors.Add(elementType, new Dictionary());
_propertyDescriptors[elementType].Add(attributeName, properties);
}
if (properties.Count > 0)
{
PropertyDescriptor descriptor = properties[0];
try
{
descriptor.SetValue(element, descriptor.Converter.ConvertFrom(document, CultureInfo.InvariantCulture, attributeValue));
}
catch
{
Trace.TraceWarning(string.Format("Attribute '{0}' cannot be set - type '{1}' cannot convert from string '{2}'.", attributeName, descriptor.PropertyType.FullName, attributeValue));
}
}
else
{
//check for namespace declaration in svg element
if (string.Equals(element.ElementName, "svg", StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(attributeName, "xmlns", StringComparison.OrdinalIgnoreCase)
|| string.Equals(attributeName, "xlink", StringComparison.OrdinalIgnoreCase)
|| string.Equals(attributeName, "xmlns:xlink", StringComparison.OrdinalIgnoreCase)
|| string.Equals(attributeName, "version", StringComparison.OrdinalIgnoreCase))
{
//nothing to do
}
else
{
//attribute is not a svg attribute, store it in custom attributes
element.CustomAttributes[attributeName] = attributeValue;
}
}
else
{
//attribute is not a svg attribute, store it in custom attributes
element.CustomAttributes[attributeName] = attributeValue;
}
}
}
///
/// Contains information about a type inheriting from .
///
[DebuggerDisplay("{ElementName}, {ElementType}")]
internal sealed class ElementInfo
{
///
/// Gets the SVG name of the .
///
public string ElementName { get; set; }
///
/// Gets the of the subclass.
///
public Type ElementType { get; set; }
///
/// Initializes a new instance of the struct.
///
/// Name of the element.
/// Type of the element.
public ElementInfo(string elementName, Type elementType)
{
this.ElementName = elementName;
this.ElementType = elementType;
}
///
/// Initializes a new instance of the class.
///
public ElementInfo()
{
}
}
}
}