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; } }