private async Task <IElement> ParseElementAsync( IElement entry, IElement parent, XmlReader reader, string version, ulong ignoreFlags, string parentTree, int elementIndex, CancellationToken cancel) { if (cancel.IsCancellationRequested) { return(null); } //DateTime startTime = DateTime.Now; Type elementType = entry.GetType(); entry.Parent = parent; entry.ElementIndex = elementIndex; entry.PreRead(); string parentElementName = reader.Name; if (string.IsNullOrEmpty(parentElementName)) { throw new Exception("Null parent element name."); } parentTree += parentElementName + "/"; entry.Tree = parentTree; if (reader.NodeType != XmlNodeType.Element) { Engine.PrintLine("Encountered an unexpected node: {0} '{1}'", reader.Name, reader.NodeType.ToString()); await reader.SkipAsync(); entry.PostRead(); return(entry); } if (entry.ParentType != typeof(IElement) && !entry.ParentType.IsAssignableFrom(parent.GetType())) { Engine.PrintLine("Parent mismatch. {0} expected {1}, but got {2}", elementType.GetFriendlyName(), entry.ParentType.GetFriendlyName(), parent.GetType().GetFriendlyName()); await reader.SkipAsync(); entry.PostRead(); return(entry); } if ((ignoreFlags & entry.TypeFlag) != 0) { await reader.SkipAsync(); entry.PostRead(); return(entry); } #region Read attributes MemberInfo[] members = entry.WantsManualRead ? null : elementType.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (reader.HasAttributes) { while (reader.MoveToNextAttribute()) { if (cancel.IsCancellationRequested) { return(null); } string name = reader.Name; string value = reader.Value; if (entry.WantsManualRead) { entry.ManualReadAttribute(name, value); } else { MemberInfo info = members.FirstOrDefault(x => { var a = x.GetCustomAttribute <Attr>(); return(a == null ? false : string.Equals(a.AttributeName, name, StringComparison.InvariantCultureIgnoreCase)); }); if (info == null) { //Engine.PrintLine("Attribute '{0}[{1}]' not supported by parser. Value = '{2}'", parentTree, name, value); } else if (info is FieldInfo field) { field.SetValue(entry, value.ParseAs(field.FieldType)); } else if (info is PropertyInfo property) { property.SetValue(entry, value.ParseAs(property.PropertyType)); } } } } #endregion if (entry is IVersion v) { version = v.Version; } entry.OnAttributesRead(); #region Read child elements if (cancel.IsCancellationRequested) { return(null); } reader.MoveToElement(); if (entry is IStringElement stringEntry) { stringEntry.GenericStringContent = Activator.CreateInstance(stringEntry.GenericStringType) as BaseElementString; stringEntry.GenericStringContent.ReadFromString(await reader.ReadElementContentAsStringAsync()); } else { if (reader.IsEmptyElement) { await reader.ReadAsync(); } else { reader.ReadStartElement(); int childIndex = 0; ChildInfo[] childElements = entry.WantsManualRead ? null : elementType.GetCustomAttributesExt <ElementChild>().Select(x => new ChildInfo(x)).ToArray(); MultiChildInfo[] multiChildElements = entry.WantsManualRead ? null : elementType.GetCustomAttributesExt <MultiChild>().Select(x => new MultiChildInfo(x)).ToArray(); //Read all child elements while (reader.NodeType != XmlNodeType.EndElement) { if (cancel.IsCancellationRequested) { return(null); } if (reader.NodeType != XmlNodeType.Element) { await reader.SkipAsync(); continue; } string elementName = reader.Name; if (string.IsNullOrEmpty(elementName)) { throw new Exception("Null element name."); } if (entry.WantsManualRead) { IElement e = entry.ManualReadChildElement(elementName, version); if (e == null) { //Engine.PrintLine("Element '{0}' not supported by parser.", parentTree + elementName + "/"); await reader.SkipAsync(); } else { await ParseElementAsync(e, entry, reader, version, ignoreFlags, parentTree, childIndex, cancel); } } else { bool isUnsupported = elementType.GetCustomAttributes <UnsupportedElementChild>(). Any(x => string.Equals(x.ElementName, elementName, StringComparison.InvariantCultureIgnoreCase)); if (isUnsupported) { if (string.IsNullOrEmpty(elementName)) { throw new Exception("Null element name."); } //Engine.PrintLine("Element '{0}' not supported by parser.", parentTree + elementName + "/"); await reader.SkipAsync(); } else { int typeIndex = -1; foreach (ChildInfo child in childElements) { if (cancel.IsCancellationRequested) { return(null); } typeIndex = Array.FindIndex(child.ElementNames, name => name.Matches(elementName, version)); //If no exact name matches, find a null name child element. //This means the class is for an element with ANY name. if (typeIndex < 0) { typeIndex = Array.FindIndex(child.ElementNames, name => name.Name == null && name.VersionMatches(version)); } if (typeIndex >= 0) { if (++child.Occurrences > child.Data.MaxCount && child.Data.MaxCount >= 0) { Engine.PrintLine("Element '{0}' has occurred more times than expected.", parentTree); } IElement elem = await ParseElementAsync(child.Types[typeIndex], entry, reader, version, ignoreFlags, parentTree, childIndex, cancel); elem.ElementName = elementName; break; } } if (typeIndex < 0) { if (cancel.IsCancellationRequested) { return(null); } int i = 0; MultiChildInfo info = multiChildElements.FirstOrDefault(c => { for (i = 0; i < c.Data.Types.Length; ++i) { ElementName name = c.ElementNames[i]; if (name.Matches(elementName, version)) { ++c.Occurrences[i]; return(true); } } return(false); }); if (info == null) { //Engine.PrintLine("Element '{0}' not supported by parser.", parentTree + elementName + "/"); await reader.SkipAsync(); } else { IElement elem = await ParseElementAsync(info.Data.Types[i], entry, reader, version, ignoreFlags, parentTree, childIndex, cancel); elem.ElementName = elementName; } } } } ++childIndex; } if (!entry.WantsManualRead) { ElementName[] underCounted = childElements. Where(x => x.Occurrences < x.Data.MinCount). SelectMany(x => x.ElementNames). Where(x => x.VersionMatches(version)).ToArray(); if (underCounted.Length > 0) { foreach (ElementName c in underCounted) { Engine.PrintLine("Element '{0}' has occurred less times than expected.", c.Name); } } } if (reader.Name == parentElementName) { reader.ReadEndElement(); } else { throw new Exception("Encountered an unexpected node: " + reader.Name); } } } #endregion entry.PostRead(); //TimeSpan elapsed = DateTime.Now - startTime; //if (elapsed.TotalSeconds > 1.0f) // if (entry is IID id && !string.IsNullOrEmpty(id.ID)) // Engine.PrintLine("Parsing {0}{2} took {1} seconds.", parentTree, elapsed.TotalSeconds.ToString(), id.ID); // else // Engine.PrintLine("Parsing {0} took {1} seconds.", parentTree, elapsed.TotalSeconds.ToString()); return(entry); }