protected override void Parse()
        {
            document = XDocument.Load(templateFile.FullName, LoadOptions.PreserveWhitespace);

            rootPart = new XMLTemplatePart(document.Root);
            rootPart.Parse();
            Root = rootPart;
        }
        private void ParseElement(XElement element)
        {
            // Scan for comments
            // For each token comment, add it to token collection
            // For each start of template, next node is the template
            // - There should only be one between start and end of a template
            // Then also check the attributes for token replacements

            bool atleastOneReplacement   = false;
            List <XAttribute> attributes = new List <XAttribute>();

            foreach (XAttribute attribute in element.Attributes())
            {
                if (TryParseAttributeValueForTokens(attribute.Name, attribute.Value, out XmlAttributeTemplateToken toReplace))
                {
                    attributes.Add(toReplace);

                    atleastOneReplacement = true;
                }
                else
                {
                    attributes.Add(attribute);
                }
            }

            if (atleastOneReplacement)
            {
                element.ReplaceAttributes(attributes);
            }

            List <Action> replacementActions = new List <Action>();
            string        templateStartKey   = null;
            bool          seenTemplateBody   = false;

            foreach (XNode node in element.Nodes())
            {
                if (node is XComment comment)
                {
                    switch (GetTokenType(comment, out string tokenKey))
                    {
                    case TokenType.ReplacementToken:
                        XmlCommentTemplateToken xmlCommentTemplateToken = new XmlCommentTemplateToken(comment.Value);
                        AddReplacementToken(tokenKey, xmlCommentTemplateToken);
                        replacementActions.Add(() => comment.ReplaceWith(xmlCommentTemplateToken));
                        break;

                    case TokenType.TemplateStart:
                        templateStartKey = tokenKey;
                        seenTemplateBody = false;
                        break;

                    case TokenType.TemplateEnd:
                        if (templateStartKey == null)
                        {
                            throw new InvalidOperationException($"We have template end '{tokenKey}' without equivalent start.");
                        }
                        else if (!Equals(templateStartKey, tokenKey))
                        {
                            throw new InvalidOperationException($"We have template end '{tokenKey}' but the template start was different '{templateStartKey}'.");
                        }
                        else if (!seenTemplateBody)
                        {
                            throw new InvalidOperationException($"We have template '{tokenKey}' without a body.");
                        }
                        else
                        {
                            templateStartKey = null;
                            seenTemplateBody = false;
                        }
                        break;
                    }
                }
                else if (node is XElement childElement)
                {
                    if (templateStartKey != null)
                    {
                        if (seenTemplateBody)
                        {
                            throw new InvalidOperationException($"Template '{templateStartKey}' can only have one body node.");
                        }
                        else
                        {
                            XMLTemplatePart templatePart = new XMLTemplatePart(childElement);
                            templatePart.Parse();
                            templates.Add(templateStartKey, templatePart);
                            replacementActions.Add(() => childElement.ReplaceWith(templatePart));
                            seenTemplateBody = true;
                        }
                    }
                    else
                    {
                        ParseElement(childElement);
                    }
                }
            }

            replacementActions.ForEach(t => t());
        }