using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml;
using Chernobyl.Collections.Generic.Event;
using Chernobyl.Reflection.Template.Attribution;
using Chernobyl.Reflection.Template.CodeDom;
namespace Chernobyl.Reflection.Template.Xml
{
///
/// An that is generated from or can be written to
/// an XML stream.
///
public class XmlInstance : InstanceDecorator
{
///
/// Initializes a new instance of the class.
///
/// The instance that
/// takes and sets service.
/// The to read the instances
/// from.
/// Thrown if the
/// is not positioned on an
/// (see ) of
/// .
public XmlInstance(IEventCollection services, XmlReader xmlr)
: base(false)
{
if (xmlr.NodeType != XmlNodeType.Element)
throw new ArgumentException("Unable to extract the XML instance data because " +
"The XmlReader passed into this constructor is not positioned on an " +
"XML Element. Please ensure the XmlReader is positioned correctly.");
// Grab the namespace/assembly and form the full type name.
NamespaceAssembly namespaceAssembly = new NamespaceAssembly(xmlr.NamespaceURI);
string typeName = xmlr.LocalName;
string fullTypeName = namespaceAssembly.Namespace + "." + typeName;
// If this is a generic instance, we'll need to grab the generic
// parameters.
string attributeValue = xmlr.GetAttribute(TypesAttribute.AttributeName, Fallout.NamepaceUri);
Type[] genericTypes = null;
if(attributeValue != null)
{
TypesAttribute typesAttributes = new TypesAttribute(xmlr, attributeValue);
genericTypes = typesAttributes.Types.ToArray();
fullTypeName = fullTypeName + '`' + genericTypes.Length;
}
// We need to locate the type. First locate the assembly and then
// locate the type in that assembly.
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
Assembly assembly = assemblies.First(asm => AssemblyName.ReferenceMatchesDefinition(asm.GetName(), namespaceAssembly.Assembly) == true);
Type type = assembly.GetType(fullTypeName, true);
// If the element contains a generic type parameter list then we are
// dealing with a generic type
if (genericTypes != null)
type = type.MakeGenericType(genericTypes);
// Now set the _decoratedInstance and tell the ComponentDecorator
// to configure it.
_decoratedInstance = new Instance(services, string.Empty, type);
ConfigureImplementer();
bool hasChildren = !xmlr.IsEmptyElement;
// grab the attributes
foreach(IAttribute attribute in XmlAttribute.Create(services, xmlr, new XmlIdAttributeProcessor(this)))
ComponentChildren.Add(attribute);
// go to the next element which will be a sibling or a child node
xmlr.MoveToElement();
bool finished;
do
{
finished = !xmlr.Read();
} while (xmlr.NodeType != XmlNodeType.Element && xmlr.NodeType != XmlNodeType.Text && finished == false);
// read in all of the members
if (hasChildren == true && xmlr.NodeType != XmlNodeType.None)
{
foreach (IMember member in XmlMember.Create(services, this, xmlr))
ComponentChildren.Add(member);
}
}
///
/// Creates a collection of instances by
/// reading them from an .
///
/// The instance that
/// takes and sets service.
/// The to read the instances
/// from.
/// The s that have been created.
public static IInstance[] Create(IEventCollection services, XmlReader xmlr)
{
List arguments = new List();
if (xmlr.HasValue == true)
{
string value = xmlr.Value;
bool parsing = true;
do
{
int index = value.IndexOfAny(new[] {',', '"'});
index = index == -1 ? value.Length - 1 : index;
if (value.Trim().Length != 0)
{
if (value[index] == '"') // is this a String?
{
int nextQuote = value.IndexOf('"', index + 1);
// create the instance from the string
IInstance instance = new PrimitiveInstance(services,
value.Substring(index, nextQuote - (index - 1)), typeof(String));
arguments.Add(instance);
// remove the rest of the string
int removeCount = value.IndexOf(',', nextQuote);
// Remove the arguments that we have processed thus
// far. If the string does not have any more
// arguments, remove everything
value = removeCount == -1 ? string.Empty : value.Remove(index, removeCount);
}
else
{
// This is not a String but some other type of
// primitive (like a number) or an instance that is
// being linked to.
// Get the value of the argument
int nextComma = value.IndexOf(',');
int readCount = nextComma == -1 ? value.Length : nextComma + 1;
string argValue = value.Substring(0, readCount).Trim().Trim(',');
// Determine whether it is a primitive number or a
// linked instance. Note that we remove the whitespace
// and an 'f' from the end when checking for numbers
// in case the number is a float. Double.TryParse
// cannot handle 'f' characters in the string.
string fFreeNumber = argValue.TrimEnd().TrimEnd('f');
double doublResult;
if (Double.TryParse(fFreeNumber, out doublResult) == true)
arguments.Add(new PrimitiveInstance(services, argValue));
else
arguments.Add(new LinkedInstance(services, new XlinkHref(argValue), new Instance(services)));
value = value.Remove(0, readCount);
}
}
else
parsing = false;
} while (parsing == true);
// now that we have finished reading this element, move past it
xmlr.Skip();
}
else
{
int startDepth = xmlr.Depth;
bool finished = false;
do
{
// get to the element if we aren't already on one
if (xmlr.NodeType == XmlNodeType.Element)
{
// Check for the XLink attribute before we load in the
// XmlInstance since the XmlInstance will move past the
// current element.
string xlinkHrefValue = xmlr.GetAttribute(XlinkHref.AttributeName, XlinkHref.AttributeUri);
IInstance instance = new XmlInstance(services, xmlr);
if (xlinkHrefValue != null)
instance = new LinkedInstance(services, new XlinkHref(xlinkHrefValue), instance);
else
instance = new CodeDomInstance(instance);
arguments.Add(instance);
}
else
finished = !xmlr.Read();
} while (xmlr.Depth >= startDepth && finished == false);
}
return arguments.ToArray();
}
///
/// A struct that can parse an XLink href attribute on an XML element
/// into its necessary parts.
///
public struct XlinkHref
{
///
/// Initializes a new instance of the struct.
///
/// The value of the XLink href attribute.
public XlinkHref(string xlinkValue) : this()
{
// Grab the XPointer, if it exists
int xpointerStartIndex = xlinkValue.IndexOf('#');
if (xpointerStartIndex == -1)
{
xpointerStartIndex = xlinkValue.Length;
Name = string.Empty;
}
else
Name = xlinkValue.Substring(xpointerStartIndex + 1);
// Grab the XLink URI.
Path = xlinkValue.Substring(0, xpointerStartIndex);
// Grab the member pointed to, if it exists
Member = string.Empty;
if(Name.Length != 0)
{
int xpointerMemberStartIndex = Name.IndexOf('/');
if (xpointerMemberStartIndex != -1)
{
Member = Name.Substring(xpointerMemberStartIndex + 1);
Name = xlinkValue.Substring(xpointerStartIndex + 1, xpointerMemberStartIndex);
}
}
}
///
/// The name used by XLink's href attribute, which is "href".
///
public const string AttributeName = "href";
///
/// The URI used by XLink attributes, which is
/// "http://www.w3.org/1999/xlink".
///
public const string AttributeUri = "http://www.w3.org/1999/xlink";
///
/// The location of the XLink.
///
public string Path { get; private set; }
///
/// The name of the instance accessed by the XLink.
///
public string Name { get; private set; }
///
/// The member that was accessed on the XLink or
/// if the
///
public string Member { get; private set; }
}
///
/// The instance that implements the
/// functionality of this class.
///
public override IInstance DecoratedInstance
{
get { return _decoratedInstance; }
}
///
/// The backing field to .
///
IInstance _decoratedInstance;
}
}