-
Tebjan Halm authored5360393d
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
namespace Svg
{
/// <summary>
/// Provides the methods required in order to parse and create <see cref="SvgElement"/> instances from XML.
/// </summary>
internal class SvgElementFactory
{
private static List<ElementInfo> availableElements;
/// <summary>
/// Gets a list of available types that can be used when creating an <see cref="SvgElement"/>.
/// </summary>
private static List<ElementInfo> 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;
}
}
/// <summary>
/// Creates an <see cref="SvgDocument"/> from the current node in the specified <see cref="XmlTextReader"/>.
/// </summary>
/// <param name="reader">The <see cref="XmlTextReader"/> containing the node to parse into an <see cref="SvgDocument"/>.</param>
/// <exception cref="ArgumentNullException">The <paramref name="reader"/> parameter cannot be <c>null</c>.</exception>
/// <exception cref="InvalidOperationException">The CreateDocument method can only be used to parse root <svg> elements.</exception>
public static SvgDocument CreateDocument(XmlTextReader reader)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
if (reader.LocalName != "svg")
{
throw new InvalidOperationException("The CreateDocument method can only be used to parse root <svg> elements.");
}
return (SvgDocument)CreateElement(reader, true, null);
}
/// <summary>
/// Creates an <see cref="SvgElement"/> from the current node in the specified <see cref="XmlTextReader"/>.
/// </summary>
/// <param name="reader">The <see cref="XmlTextReader"/> containing the node to parse into a subclass of <see cref="SvgElement"/>.</param>
/// <param name="document">The <see cref="SvgDocument"/> that the created element belongs to.</param>
/// <exception cref="ArgumentNullException">The <paramref name="reader"/> and <paramref name="document"/> parameters cannot be <c>null</c>.</exception>
public static SvgElement CreateElement(XmlTextReader reader, SvgDocument document)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
}
if (document == null)
{
throw new ArgumentNullException("document");
}
return CreateElement(reader, false, document);
}
private static SvgElement CreateElement(XmlTextReader reader, bool fragmentIsDocument, SvgDocument document)
{
SvgElement createdElement = null;
string elementName = reader.LocalName;
//Trace.TraceInformation("Begin CreateElement: {0}", elementName);
if (elementName == "svg")
{
createdElement = (fragmentIsDocument) ? new SvgDocument() : 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);
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
}
//Trace.TraceInformation("End SetAttributes");
}
private static Dictionary<Type, Dictionary<string, PropertyDescriptorCollection>> _propertyDescriptors = new Dictionary<Type, Dictionary<string, PropertyDescriptorCollection>>();
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<string, PropertyDescriptorCollection>());
_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
{
//attribute is not a svg attribute, store it in custom attributes
element.CustomAttributes[attributeName] = attributeValue;
}
}
/// <summary>
/// Contains information about a type inheriting from <see cref="SvgElement"/>.
/// </summary>
[DebuggerDisplay("{ElementName}, {ElementType}")]
internal sealed class ElementInfo
{
/// <summary>
/// Gets the SVG name of the <see cref="SvgElement"/>.
/// </summary>
public string ElementName { get; set; }
/// <summary>
/// Gets the <see cref="Type"/> of the <see cref="SvgElement"/> subclass.
/// </summary>
public Type ElementType { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ElementInfo"/> struct.
/// </summary>
/// <param name="elementName">Name of the element.</param>
/// <param name="elementType">Type of the element.</param>
public ElementInfo(string elementName, Type elementType)
{
this.ElementName = elementName;
this.ElementType = elementType;
}
/// <summary>
/// Initializes a new instance of the <see cref="ElementInfo"/> class.
/// </summary>
public ElementInfo()
{
}
}
}
}