Пример #1
0
    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);
    }